/
Author: Григорьев А.
Tags: математическое программирование математика компьютерные технологии язык программирования c
ISBN: 5-86404-021-5
Year: 1992
Text
Автор серии "С для PC"
АНДРЕЙ ГРИГОРЬЕВ
работает в секторе
математического
моделирования биологических
процессов ВНИИГенетики
(Москва). Выпускник
Московского инженерно-
физического института.
Обладатель четырех
золотых медалей Первой
и Второй Всемирных
компьютерных Олимпиад,
проходивших в 1989
и 1990 годах в Лондоне.
Один из организаторов
Всесоюзной
компьютерной Олимпиады. Автор
ряда публикаций
в зарубежных
компьютерных журналах, таких
как "Computer Applications
in the Biosciences"
и "Advantage"
(Великобритания), TUG Lines
(США). С 1989 года ведет
постоянную рубрику в
журнале "С Vu" (С Users
Group, Великобритания),
освещающем различные
вопросы программирования
на языке С.
РО
АНДРЕЙ ГРИГОРЬЕВ
ПРОГРАММИРОВАНИЕ
НА ЯЗЫКЕ С
ДЛЯ ПЕРСОНАЛЬНЫХ
КОМПЬЮТЕРОВ
Выпуск 8 .
Плоттер и диджитайзер
Выпуск 9
Мышь
МОСКВА ■ "ДИАЛОГ-МИФИ" ■ 1992
ббк 22.18 Серия "С для PC. Программирование
Г83 на языке С для персональных
УДК 519.85 компьютеров" издается с 1990 года
Григорьев А.
Г83 Плоттер и диджитайзер. Мышь.— М.: "ДИАЛОГ-МИФИ",
1992. — 112 с— (Серия "С для PC. Программирование на
языке С для персональных компьютеров"; Вып. 8 и 9)
ISBN 5-86404-021-5 (Вып. 8 и 9)
Издательство сочло возможным объединить два последних выпуска
серии "С для PC под одной обложкой.
В восьмом выпуске рассматриваются вопросы управления диджитайзе-
ром и плоттером; подробно объясняется назначение и использование ряда
опций компилятора Microsoft С; приводится описание опций
компоновщика Microsoft Overlay Linker (версия 3.65).
В девятом выпуске рассматриваются вопросы управления
манипулятором "мышь"; также описаны дополнительные возможности управления
компоновкой программ с помощью компоновщика LINK, входящего в пакет
Microsoft С; приводится описание сообщений об ошибках компоновщика и
утилиты МАКЕ.
Для широкого круга пользователей персональных компьютеров.
2404010000-005
Г70(03)-92
Научное издание
ГРИГОРЬЕВ Андрей
ПЛОТТЕР И ДИДЖИ
Серия "С для PC. Программирование на языке и
для персональных компьютеров", выпуски 8 и 9
Художественный редактор О.А. Кузьмшюва
Технический редактор О.А. Красильникова
Корректор B.C. Кустов
Подписано в печать 14.03.92. Усл. печ. л. 6,51. Уч.-изд. л. 4,46.
Тираж 15 000 экз. Заказ Зт?3
Акционерное общество "ДИАЛОГ-МИФИ"
115409 Москва, ул. Москворечье, 31, корп. 2
Подольский филиал Чеховского полиграфического комбината ПО "Периодика"
142100, г. Подольск, Московская обл., ул. Кирова, 25
\~™ 5Л^4Л1\'5 (Вып- 8 и 9) © Андрей Григорьев, 1992
Оригинал-макет, оформл
АО "ДИАЛОГ-МИФИ", 1992
ISBN 5-86404-003-7 @ Оригинал-макет, оформление обложки.
ОГЛАВЛЕНИЕ
Ныпуск 8
Стоит ли покупать эту книгу? 3
ПЛОТТЕР И ДИДЖИТАЙЗЕР 5
Вопросы и задания , 30
ЕЩЕ НЕМНОГО ОБ ОПЦИЯХ КОМПИЛЯТОРА 31
Генерация кода 31
Выходные файлы и листинги 34
Препроцессор 38
Язык и отладка 40
Операции с плавающей точкой ... 42
Разное 44
ПРИЛОЖЕНИЕ А 45
Использование опций компоновщика LINK 45
Выпуск 9
МЫШЬ 62
ДОПОЛНИТЕЛЬНЫЕ ВОЗМОЖНОСТИ
КОМПОНОВЩИКА 84
Текстовая и бинарная моды чтения-записи 84
Управление размещением стека
и динамической области памяти 84
Использование оверлейных структур 86
ПРИЛОЖЕНИЕ А 89
Сообщения об ошибках компоновщика 89
ПРИЛОЖЕНИЕ В 103
Сообщения об ошибках утилиты МАКЕ 103
ПРИЛОЖЕНИЕ С 107
Список функций драйвера мыши 107
Функции библиотеки регистрового
интерфейса EGA (EGA Register Interface Library) ...108
1
3
СТОИТ ЛИ ПОКУПАТЬ ЭТУ КНИГУ?
Что это такое?
"С для PC" - это серия выпусков, освещающих разнообразные
вопросы программирования на языке С для персональных
компьютеров. В выпусках "С для PC" вы сможете найти наряду с
учебной и полезную справочную информацию.
Кому это нужно?
Материал, предлагаемый вашему вниманию в серии "С для PC",
рассчитан на широкий круг читателей. Наиболее полезным, по-
видимому, он окажется для программистов, имеющих некоторые
начальные знания как о самом языке С, так и об устройстве
персонального компьютера фирмы IBM.
Впрочем, и новички, и профессионалы наверняка найдут в
каждом выпуске новую и полезную для себя информацию.
Что содержится в этой книге?
В восьмом выпуске серии "С для PC" рассматриваются вопросы
управления диджитайзером и плоттером. Также подробно
объясняется назначение и использование некоторых важных опций
компилятора Microsoft С. Кроме того, здесь приводится описание
опций компоновщика Microsoft Overlay Linker (версия 3.65).
В девятом выпуске серии "С для PC" рассматриваются вопросы
управления манипулятором "мышь". В этом выпуске также
уделено внимание дополнительным возможностям управления
компоновкой программ с помощью компоновщика LINK, входящего в
пакет Microsoft С.
Кроме того, здесь приводится описание сообщений об ошибках
компоновщика и утилиты МАКЕ.
4
ПЛОТТЕР И ДИДЖИТАЙЗЕР
Диджитайзер (или иначе - дигитайзер, или оцифровыватель, -
все это должно быть эквивалентно английскому digitizer) - не
слишком часто используемое периферийное устройство. Впрочем,
он незаменим в тех случаях, когда вам необходимо ввести в
компьютер точные данные, представленные в двухмерном виде.
Графическое изображение на бумаге проще вводить с помощью
сканнера, однако для получения, скажем, точных относительных
координат точек возникает необходимость анализа TIFF-файлов,
тогда как ввод диджитайзера - не что иное, как координаты
точек. Кроме того, отнюдь не любое изображение "влезет" в скан-
нер, например проекция диапозитива или тонкая пластина.
Остановимся на одной разновидности диджитайзеров - TRUE
GRID, выпускаемых фирмой Houston Instruments. Они
представляют собой панель размером от 130x130 до 280x430 мм и
снабжаются курсорами в виде пера или напоминающей "мышь"
коробочки с лупой, перекрестьем и одной или несколькими клавишами.
С этой разновидностью мне пришлось больше всего работать,
когда я писал программу для ввода и обработки результатов
некоторых молекулярнобиологических экспериментов, позволяющих
определять размеры молекул ДНК.
Другой популярной разновидностью диджитайзеров является
продукция фирмы Hewlett-Packard. Популярны также диджитайзеры
Summa, Kurta, Geographic. Существенная разница между этими
моделями лишь в наборе управляющих команд. Набор команд
для диджитайзера TRUE GRID приведен в файле <andy\digit.h>:
/а************************************************************/
/* V
/* <andy\digit.h> '- header file for digitizer programming. */
/* V
У*************************************************************/
5
/* СОМ-порты 7
«define _COM_1
«define _С0М_2
«define _C0M_3
0
1
2
/* Клавиши диджитайзера */
«define NOJUTTON
«define
«define
«define
«define
«define
«define
«define
«define
«define
«define
«define
BUTTONJ
WHITE_B
GREEN_B
YELLOWJ
RED_B
ANY_B
RELEASED
PRESSED
OUT_PROX
FIRST_CO
NEXT_CO
OxOO
0x01
BUTTON,1
0x02
0x03
0x04
0x04
0x00
0x01
0x02
0x03
0x04
«define BUTTON_MASK. 0x3F
/* Управление диджитайзером */
«define ESC
«define XOFF
«define XON
«define _STOP
«define I_STOP ,
0x1B
0x19
0x17
'S'
'Q'
/* Режимы работы диджитайзера */
/* Если указана цифра, она обозначает число */
/* координат точек, пересылаемых за 1 секунду */
/* Точка (абсолютные координаты */
/* при нажатии клавиши) "Point" */
«define POINT 'P'
/* Триггер (абсолютные координаты
/* по запросу компьютера) "Triggered"
«define TRIG 'Г
/* Переключаемый поток (абсолютные
/* координаты - непрерывная передача
/* при нажатии клавиши) "Switch stream
«define STSW_2 '@'
«define STSW_5 'A'
«define STSWJ0 'B'
«define STSW_20 'C
«define STSW_50 'D'
«define STSWJ00 'E
/* Поток (абсолютные координаты - .
/* непрерывная передача) "Stream"
«define ST_2 'H-
«define ST_5 T
«define ST_10 'J'
«define ST_20 'K'
«define ST_50 'L'
«define STJ00 'M'
/* Дельта (относительные координаты -
/* непрерывная передача) "Delta"
«define DE_2 'X'
«define DE_5 'Y'
«define DE_10 '!'
«define DE_20 '['
«define DE_50 'V
«define DE_100 ']'
/* Формат данных */
«define BINARY_ "p"
«define A STRING 'r'
#dofiiio I_STRING ■'?,'
/* Начало коордииа! */
«defino 0RI_LLFT 'I '
«define 0RI_ULFT 'u'
/* Терминирующий символ */
«define CRLF_TERM
«definc CR_TERM 'a'
«define LF_TERM 'b'
«define N0_TERM 'c'
/* Масштабирование */
«define SCALE_5 'd'
«define SCALE_10 'e'
«define SCALE_1_27 T
«define SCALE_2_54 'g'
«define SCALE_INC 'h'
/* Тип усреднения (встроенный) */
«define AVG 1
«define AVG 2
«define AVG 0
'x
'y
'z
int dig_button(char, int *);
void send(char *, int);
Г V
/* Конец файла <andy\digit.h> */
/* 7
8
Как видно из текста этого header-файла, существует несколько
режимов работы диджитайзера, отличающихся смыслом
передаваемых координат.
В режиме "точка" (Point) диджитайзер передает абсолютные
координаты точки, в которой находится курсор, при нажатии
клавиши. В режиме "триггер" (Triggered) - абсолютные координаты
точки по запросу компьютера. Существуют два потоковых
режима. Обычный "поток" (Stream) - это непрерывная передача
абсолютных координат, а "переключаемый поток" (Switch stream) -
тот же вид передачи, включаемый при нажатии клавиши. В
режиме "дельта" (Delta) также происходит непрерывная передача,
но - относительных координат.
Кроме того, существует три различных типа формата
передаваемых данных: бинарная передача, ASCII-строка и целочисленный
ASCII формат. Они отличаются способом записи возвращаемых
диджитайзером координат точки.
Формат ASCII-строки для передачи данных выглядит следующим
образом:
CB + XXXXX + YYYYYCRLF
L_ I I I I I I I I I I I I I I I
Для целочисленного ASCII формата порядок следования байтов
будет несколько иным:
X X X X , Y Y Y Y , СВ CR LF
I I I i i i i I I I i ' i I
В обоих приведенных выше форматах X и Y обозначают цифры,
составляющие числовые значения координат X и Y (в ASCII
форме); символы "плюс" и "запятая" - сами себя; CR и LF -
соответственно символы возврата каретки и перехода на новую строку;
а СВ - так называемый контрольный байт.
Значение этого контрольного байта наиболее просто определяется
для цслочиспенного ASCII формата: оно попросту равно одной из
констант, обозначающих клавиши диджитайзера в header-файле
<andy\digit.h> - NO_BUTTON, WHITE_B (эквивалентной
BUTTON_l для пера или однокнопочного курсора), GREEN_B,
YELLOW В или RED В.
2 Swr. 3*9
9
Для формата ASCII-строки существует целая таблица значений
контрольного байта (в зависимости от режима работы диджитай-
зсра). Я не стану здесь ее приводить, ограничившись текстом
функции dig_button(), возвращающей соответствующее состояние
клавиши и ее цвет.
«include <andy\digit.h>
/* ■ V
/* DESCRIPTION: "dig_button" determines a <button> state, */
/* according to <control_byte> value. */
/* ARGUMENTS: button pointer and control byte. */
/* RETURN: state of button. */
/* GLOBAL VARS REFERENCED: none. */
/* GLOBAL VARS MODIFIED: none. */
/* V
у*******.******************************************************/
int dig_button(char control_byte, int * button)
<
int state;
switch(control_byte) {
case '3'
case.'5'
case '7'
button = ANY_B;
state = RELEASED;
break;
case '4'
case '6'
case '8'
state = PRESSED;
button = WHITE_B;
break;
case '9' :
button = ANY Б.
10
state = 0UT_PR0X;
break;
case '0' :
state = FIRST_C0;
button = WHITE_B;
break;
case '1' :
button = WHITE_B;
break;
case '2' :
button = WHITE_B;
break;
case '@' :
state = FIRST_C0;
button = RED_B;
break;
case 'A' :
case 'B' : .
state = NEXT_C0:
button = RED_B;
break;
case 'D' :
case 'F' :
case 'H' :
state = PRESSED;
button = RED_B;
break;.
case 'V :
state = FIRST.CO;
button = YELL0W_B;
break;
case 'a' :
case 'b' :
state = NEXT_C0;
button = YELL0W_B;
break;
case 'd' :
case 'f :
case 'h' :
state = PRESSED;
button = YELLOWJ;
break;
case 'P' :
state = FIRST_C0;
button = GREENJ;
break;
case 'Q' :
case 'R' :
state = NEXT_C0;
button = GREENJ;
break;
case 'T' .
case 'V :
case 'X' :
state = PRESSED;
button = GREENJ;
break;
default:
button = N0_BUTT0N;
state = -1;
break;
return state;
Бинарный формат передачи данных выглядит совсем по-другому.
Он представляет собой блок, состоящий из пяти байт: одного -
для контрольного блока (СВ) и по два байта для каждой
координаты (ХА-В и YA-B):
СВ
Р 1 n n n n О О
i i i i i i i 1 1
ХА
5 О
РОХХХХХХ
I I I I I I I 1 1
12
хв
11 6
РОХХХХХХ
I I I I I 1 I I I
YA
5 О
P0YYYYYY
I I I I I J I I I
YB
11 6
P0YYYYYY
I 1 I I L_ I J I I
Здесь Р обозначает четность, 0 или 1 - биты с обязательными
значениями, а X и Y - биты, формирующие значения
соответствующих координат. Верхние индексы указывают на порядок
следования бит (по 11 для каждой координаты).
Контрольный байт содержит четыре бита, обозначенные буквой
п, - они также указывают на нажатую клавишу диджитайзера.
пппп Значение
0000 NO_BUTTON
0001 WHITE_B (BUTTONJ)
ООН) GREEN_B •
0100 YELLOW_B
1000 RED_B
Очевидно, что определение того факта, что красная клавиша че-
тырехкнопочного курсора была нажата, будет в этом случае
выглядеть таким образом:
if((controi_byte » 2) & RED_B){
/* соответствующие действия */
>
г
13
"Склеивание" же координатных битов также достигается
несложными битовыми операциями:
х = (ха & BUTTON_MASK) | ((xb & BUTTON_MASK) «6);
у = (уа & BUTTON_MASK) | ((yb & BUTTON_MASK) « 6);
Попробуем теперь послать какую-либо команду диджитаизеру и
получить что-нибудь в ответ от него.
Посылаемую диджитаизеру команду необходимо окружить с
обеих сторон командами XOFF и XON: первая из них приостановит
его работу, с тем чтобы все "внимание" диджитайзера
сосредоточилось на вновь поступающей команде, а вторая - возобновит его
функционирование.
Между командами XOFF и XON можно поместить сразу
несколько других команд, однако это может привести иногда к
непредсказуемым результатам из-за временных задержек "восприятия"
диджитайзера, связанных с обработкой каждой команды.
Надежнее поэтому отправлять команды, по очереди помещая их между
XOFF и XON.
Упомянем еще парочку тонкостей, о которых необходимо
помнить при "кёмандовании" диджитайзером.
Команда, определяющая режим его работы, должна быть
последней в списке поочередно передаваемых команд - в противном
случае диджитаизер тотчас по ее получении начнет отправлять
данные о координатах и может пропустить какие-либо из
последующих команд.
После передачи диджитаизеру команды ESC необходимо
подождать не менее одной секунды перед тем, как посылать или
получать что-либо от диджитайзера. Эта пауза позволит ему
корректно восстановить принятые по умолчанию параметры работы.
Текст простой функции send О, выполняющей операцию передачи
данных, приведен ниже.
14
#include <bios.h>
tfinclude <andy\time.h>
#include <andy\digit.h>
/* V
/* DESCRIPTION: "send" sends a <data> string to digitizer,*/
/* attached to <port> specified. */
/* ARGUMENTS: data string pointer and COM-port. */
/* RETURN: void. */
/* GLOBAL VARS REFERENCED: none. */
/* GLOBAL VARS MODIFIED: none. */
/* V
у*************************************************************/
void send(char *data, int port)
{
while(*data){
_b ios_se rialcom(_C0M_SEND,port,XOFF);
_bios_serialcom(_COM_SEND,port,*data);
_bios_serialcom(_COM_SEND, port, XON);
if(*data == ESC)
pause(20);
data++;
>
)
Здесь мы воспользовались функцией pauseO, задерживающей
исполнение следующей итераций цикла для команды ESC на 1.1
сек (подробнее речь об этой функции шла во втором выпуске
серии "С для PC").
Для обращения к диджитаизеру используется функция Microsoft
С _bios_serialcom(), объявленная в header-файлах <dos.h> и
<bios.h>:
15
ftinclude <dos.h>
ftinclude <bios.h>
unsigned _bios_serialcom(command, port, byte)
unsigned command;
unsigned port;
unsigned byte;
Попробуем инициализировать диджитайзер, присоединенный к
порту СОМ1 и подготовить его к приему и передаче данных.
Предположим, что необходимые параметры таковы: 9600 бод, 8
бит данных, 1 стоп-бит, без проверки четности, терминирующие
символы CR и LF, формат ASCII-строки, режим "точка" и
нижний левый угол - начало координат панели диджитайзера.
Если нам нужно представить эту операцию в виде функции, то
текст ее должен выглядеть таким образом:
«include <bios.h>
«include <andy\digit.h>
void dig_ini(void)
/* ' V
/* DESCRIPTION: "dig_ini" initializes a digitizer */
/* _C0M_CHR8|_C0M_ST0P1|_C0M_N0PARITY|_C0M_9600 */
/* and sends commands 'ASCII string format', 'lower left */
•/* corner', '<CR and LF> output terminator', 'point mode' */
/* ARGUMENTS: void. */
/* RETURN: none. */
/* GLOBAL VARS REFERENCED: none. */
/* GLOBAL VARS MODIFIED- none. */
/*
7
/***************** t4,*tt*t****ti,ttttttttttitittititititittttttttttttt.
16
{
char ini_string[5];
_bios_serialcom(_COM_INIT,_COM_1,_COM_CHR8|_COM_STOPl|
_C0M_N0PARITY|_COM_9600);
/* то же, что и send("rl'P") */
ini_string[0] = A_STRING;
ini_string[1] = 0RI_LLFT;
ini_string[2] = CRLF_TERM;
ini_string[3] = POINT; /* последняя в списке */
ini_string[4] = '\0';
send(ini_string);
Итак, диджитайзер готов к работе. Предположим теперь, что нам
нужно ввести с его помощью какое-либо графическое
изображение. Оно, скажем, состоит из нескольких ломаных линий,
координаты точек излома которых мы и будем поочередно вводить.
Затем наша воображаемая программа могла бы восстановить эти
линии или использовать полученные значения координат для
каких-нибудь вычислений.
Желательно при этом иметь в нашей программе небольшой
сервис, облегчающий работу с ней: стирание неверно введенной
точки, переориентацию изображения на плоскости диджитайзера и
так далее. Эти опции можно объединить в небольшое меню.
Кроме того, вполне естественным было бы использование при работе
с этим меню не клавиатуры, а самого диджитайзера. Для этого к
поверхности панели диджитайзера нужно прикрепить табличку
меню, и нажатие на клавишу рядом с выбранной опцией меню
будет вызывать соответствующее действие программы.
Правда, мы все еще не научились получать координаты точки от
диджитайзера. Этой цели служит функция dig_string(), текст
которой приведен далее.
17
«include <bios.h>
«include <stdlib.h>
«include <andy\digit.h>
/* 7
/* DESCRIPTION: "dig_string" creates a pair of coordinates */
/* from ASCII string of digitizer. */
/* ARGUMENTS: void. 7
/* RETURN: struct POSITION (that holds a pair). 7
/* GLOBAL VARS REFERENCED: none. 7
/* GLOBAL VARS MODIFIED: nibne. V
/* 7
/«A***********************************************************/
struct POSITION dig_string(void)
{
struct POSITION pxy;
unsigned got, i;
char string[15];
while(1){
if(got = _bios_serialcom(_COM_RECEIVE,0,0) & OxFF){
string[0]= got;
for(i=1;i<15; i++){
while((got=_bios_serialcom(_COM_RECEIVE,0,0)) & OxFFOO);
string[i] = got & OxFF;
)
pxy.x = atoi(string+2);
pxy.у = atoi(string+8);
return(pxy);
8
Здесь не производится определение цвета нажатой клавиши,
поскольку в режиме "точка" для нашей задачи нас будет
интересовать только сам факт нажатия клавиши. При необходимости вы
легко сможете видоизменить эту функцию сами, добавив к ней
анализ контрольного байта.
Отметим, что функция dig_string() не передает координаты по
ссылке (что было бы естественно сделать), а возвращает
структуру, содержащую координаты точки. Это сделано для облегчения
реализации функции меню.
Пусть сама табличка меню, располагаемая на поверхности
панели диджитайзера, выглядит следующим образом:
Quit
Setup
Clear
New
Backspace
Enter
Назначение опций в этом меню таково:
Quit Завершение работы
Setup Ориентация данных на
поверхности панели диджитайзера
Clear Сброс введенной линии
New Новый набор данных
Backspace Отмена последней введенной точки
Enter Завершение ввода линии
Эти опции проще всего представить в виде массива структур,
которые содержали бы помимо строк-названий опций еще и их
координаты на панели диджитайзера. Кроме того, удобно
продублировать этот массив перечислением наименований опций (как
делалось в предыдущих выпусках) или попросту одноименными Kd-
нстантами, собранными в header-файле "menu_dig.h". (Кавычки
вместо угловых скобок в этом имени указывают на то, что этот
файл отыскивается не в списке каталогов по переменной
окружения INCLUDE, а в текущем каталоге.)
19
/* v
/* Header-файл "menu_dig.h" */
/* V
;4«4ili444*l4lll4ll44li411i444lli414*ll4444l444ii444»ii44l444l/
#define ALL 0
#define OUT -1
#define FINISHED -2
/* Опции меню */
#define QUIT О
#define SETUP 1
#define CLEAR 2
#define NEW 3
#define BACK 4
#define OK 5
#define ENTER OK
#define MAXPOINT 100
/* max frags in track */
#define MOX 400
#define MDY 200
/* item box size, x */
/* item box size, у */
struct POSITION
int x,y;
struct MENU{
char name[10];
struct POSITION place;
>;
extern struct MENU me[];
extern int xMenu.yMenu;
extern int xUL, yUL, xUR.yUR,
xLL.yLL, xLR.yLR;
/* menu names & coords */
/* menu coordinates */
/* panel coordinates */
20
extern double BETA,SI,CO; /* angle, sine, cosine */
/* Function declarations */
int dig_ini(void);
struct POSITION dig_string(void);
void read_dig(int *, int *);
void panel(void);
int in_menu(struct POSITION *);
int get_point(struct POSITION *);
void quit(void);
void clear(void);
void new(void);
void back(void);
void-setup(void);
/* 7
/* Конец файла "menu_dig.h" */
/* 7
Текст функции read_dig(), проверяющей, не была ли выбрана
какая-нибудь опция из меню, и обрабатывающей эту опцию, или
в противном случае возвращающей координаты введенной точки,
выглядит так:
Л V
/* DESCRIPTION: "read_dig" uses "injiienu" to fulfill the */
/* procedure corresponding to selected menu option or */
/* writes the coordinates of input point. */
/* ARGUMENTS: pointers to coordinates of the point. */
/* RETURN: void. */
/* GLOBAL VARS REFERENCED: none. */
21
/* GLOBAL VARS MODIFIED: none. 7
Л 7
vofd read_dig(int *x, int *y)
{ •
struct POSITION adr;
int pos;
while((pos = in_menu(adr = dig_string())) != 0UT){
sound(90,l0);
switch(pos){
case QUIT :
quit();
break;
case CLEAR :
ctear();
break;
case NEW
new();
break;
case BACK :
back();
break;
case SETUP :
setup();
break;
case OK :
*y = FINISHED;
return;
}
)
sound(50,5);
*x = adr.x;
*y = adr.y;
return;
}
22
Процедура проверки совпадения координат последней введенной
точки с координатами опций меню осуществляется простенькой
функцией in_mcnu():
/* 7
/* DESCRIPTION: "injnenu" checks whether the point input */
/* by digitizer is within menu option boxes. */
/* ARGUMENTS: struct POSITION (coordinate pair). 7
/* RETURN:. OUT - out of menu. 7
/* GLOBAL VARS REFERENCED: none. 7
/* GLOBAL VARS MODIFIED: none. 7
/* 7
int in_menu(struct POSITION p)
{
if(p.у > me[OK].ptace.y-MDY && p.y < me[QUIT].place.y+MDY) x
if(p.x > xMenu-MDX/2 && p.x < xMenu+MDX/2)
return((me[QUIT].ptace.y - p.y + MDY/2)/yMenu);
return OUT;
Константы MDX и MDY - размеры "чувствительной" области
вокруг точки, соответствующей той или иной опции меню.
Это еще не все. Чтобы функции read_dig() и in_menu() могли
работать, нам необходимо провести первоначальную "разметку"
поверхности диджитайзера. Помимо ввода координат меню нужно
определить угол наклона картинки относительно осей координат
диджитайзера, ведь нам может потребоваться проверка
параллельности линий на картинке и так далее. Этой цели служит
функция рапе!О, текст которой приведен ниже.
#include <math.h>
#inctude <ftoat.h>
23
«include <andy/use.h>
«include <andy/digit.h>
«include "menu_dig.h"
«define AVG(a,b,c,d,e, f) (((a)+(b)+(c)+(d)+(e)+(f ))/6)
/*************************************************************/
/* */
/* DESCRIPTION: "panel" sets-up a digitizer panel, menu '/
/* and picture coordinates. */
/* ARGUMENTS: void. . V
/* RETURN: void. V
/* GLOBAL VARS REFERENCED: all given above. V
/* GLOBAL VARS MODIFIED: all given above. V
Л V
/*************************************************************/
int xMenu.yMenu;
:int xUL,yUL,xUR,yUR;
double BETA,SI,CO;
struct MENU me[7] ={"Quit", 0,0,
"Setup", 0,0,
"Clear", 0,0,
"New", 0,0,
"Backspace", 0,0,
"Enter", 0,0,
ALL, 0,0};
static char menuOK = 0; /* флаг меню для повторной */
/* ориентации на панели */
void panel(void)
{
char answer;
int i, row = 1, col = 1, /* левый верхний угол меню */
rowl = 12, сои = 1, /* строка запросов */
hi = 8, wi = 13; /* габариты меню */
struct POSITION adr;
24
whiie(!menuOK){
f rame(row,col, hi.wi, V2_);
fil l(row,col,hi,wi,255);
/* point menus */
for(i=QUIT;*me[i].name;i++){
cursor_xy(row+i+1,col);
printf("%s",me[i].name);
adr = dig_string();
sound(90,10);
delay(5);
me[i].place.x = adr.x;
me[i].place.y = adr.y;
}
cursor_xy(row1,col1);
printf("Is everything OK? [y/n]");
answer = getch();
if(answer == 0)
answer = getch();
if(answer == 'Y' || answer == 'y')
menuOK = 1;
break;
}
}
xMenu = AVG(me[QUIT].place.x,
me[SETUP].place, x,
me[CLEAR].place, x,
me[NEW].place.x,
me[BACK].place.x,
me[OKJ:place.x);
yMenu = AVG(me[QUIT].place.y-me[SETUP].place.y,
me[SETUP].pi ace.y-me[CLEAR].pi ace.y,
me[CLEAR].place.y-me[NEW].place, y,
me[NEW].place.y-me[BACK].pi асе.у,
me[BACK].place.y-me[0K].place.y,
(me[QUIT].place.y-me[0K].place.y)/5);
3 3a*. 319
/* picture corners */
cursor_xy(rowl,coll);
printf("Point at the upper-left-corner");
read_dig(&xUL,&yUL);
sound(90,10);
delay(5);
cursor_xy(row1,coM);
printf("Point at the upper-right corner");
read_dig(&xUR,&yUR);
sound(90,10);
delay(5);
BETA = atan2(yUR - yUL, xUR - xUL);
SI = sin(BETA);
CO = cos(BETA);
>
Значения синуса и косинуса угла наклона картинки BETA
вычисляются в функции panel() с тем, чтобы не повторять их в
дальнейшем при определении координат вводимых точек.
В функции panel О используются несколько функций, которые
были приведены в третьем выпуске серии "С для PC".
После вызова этой функции на экране в окошке меню
поочередно появляются все шесть опций. Пользователю необходимо
указать курсором на каждую из опций в табличке, расположенной
на поверхности диджитайзера. Разумеется, зная наперед
количество опций и размер бумажного меню,, можно было бы указать
только на верхний правый и нижний левый углы таблички.
Впрочем, используемый вариант позволяет не беспокоиться о
сохранности таблички - в любой момент вы можете нарисовать ее от
руки и программа будет прекрасно с ней работать (лишь бы
расстояния между опциями не слишком отличались одно от другого).
После успешной отметки всех шести онций, сопровождающейся
звуковым подтверждением, необходимо ответить 'Y' на
следующий вопрос программы:
26
Is everything OK ? [y/n]
Эта проверка введена на случай двойного нажатия клавиши
диджитайзера или еще каких-либо недоразумений при вводе меню.
Если же все в порядке, функция panel0 усредняет координаты
опций меню и вычисляет соответствующие углы наклона
картинки, с тем чтобы координаты вводимых точек пересчитывались
правильным образом.
После этого можно вводить в программу данные с диджитайзера.
Это делает функция get_point(), приведенная ниже.
/* V
/* DESCRIPTION: "get_point" uses "read_dig" to find the */
/* coordinates of the point input by digitizer. */
/* ARGUMENTS: pointer to the coordinate structure. */
/* RETURN: OK or FINISHED to stop input. */
/* GLOBAL VARS REFERENCED: picture coordinates. */
/* GLOBAL VARS MODIFIED: none. V
/* 7
int get_frg(struct POSITION *pos)
{
int x, y;
read_dig(&x,&y);
if(y == FINISHED) return FINISHED;
pos->x = (x - xUL)*C0 - (yUL - y)*SI;
pos->y = (x - xUL)*SI + (yUL - y)*C0;.
return(OK);
}
3
27
Дальнейшее - очевидно. Функция quitO завершает работу
программы; clearO - очищает массив точек для текущей линии; new()
- подготавливает ввод данных для новой картинки; backO -
удаляет последнюю введенную точку из массива; a setupO -
вызывает функцию panel() для переориентации картинки на
поверхности панели диджитайзера при случайном сдвиге ее.
Осталась еще функция main() Ее, как и пять предыдущих, вы
легко сможете написать самостоятельно, ориентируясь на
стоящую перед вами конкретную задачу.
Я же прерву свой рассказ о диджитайзере, чтобы уделить немного
внимания другому устройству, присоединяемому к вашему
компьютеру через асинхронный порт. Это плоттер (или
графопостроитель), позволяющий вам рисовать цветные (и черно-белые,
разумеется) картинки не по точкам, как мы это делали в случае
матричного принтера, а используя непрерывные линии.
Конечно, плоттер можно заставить вывести какое-либо
изображение поточечно, но в этом случае он проигрывает по качеству
цветному матричному принтеру.
Рисование линий на плоттере выполняется чрезвычайно просто
Достаточно только передать ему соответствующую команду, цвет
и координаты точек, характеризующих линию. Эти команды
объединяются в так называемые графические языки плоттеров. Так.
набор команд плоттеров, выпускаемых фирмой Hewlett-Packard
называется HP-GL (Hewlett-Packard Graphic Language).
Для успешного управления плоттером программисту необходимо
только правильно составить ту или иную команду. Например, для
рисования отрезка прямой нужно передать плоттеру строку вида
"SP%d;LT%d;PAPU%f,%f;PD%f,%f;"
где SP обозначает выбор цвета пера (Select Pen), LT - тип линии
(Line Type), PA - работу в абсолютных координатах (Plot
Absolute), PU - установку пера в начальную позицию без
рисования (Pen Up), a PD - собственно проведение линии в заданную
точку (Pen Down).
28
Для изображения окружности вышеприведенную команду следует
лишь чуть-чуть изменить:
•'SP%d; LT%d; PAPU%f. %f; AA%f. %f, 360;"
Как вы догадались, команда АА обозначает рисование дуги с
указанным радиусом, центром и углом поворота в градусах.
Написать программу, которая с помощью функции sprintfO
соединяет все необходимые команды и вставляет разделяющие их
знаки препинания, - дело десяти минут. Для этого достаточно
знать только соответствующие аббревиатуры команд HP-GL или
другого языка (если вы работаете с другим плоттером, скажем,
Houston Instruments DMP или IBM XY).
Послать же команду плоттеру вы можете, пользуясь функцией
sendO, приведенной выше. Другой способ заключается в прямом
обращении к портам микросхемы 8250 (Universal Asynchronous
Receiver Transmitter), управляющей последовательными адапто- i
рами. Этот подход более аккуратен, так как он учитывает
возможные задержки плоттера на "размышления" над той или другой
командой во избежание потери поступающих следом команд.
Адреса портов связи хранятся начиная со смещения 400Н от
начала оперативной памяти, то есть по адресу 0000:0400 для СОМ1,
0000:0402 для COM2 и так далее до COM4. Фактический адрес,
записанный ъ 0000:0400, составляет 3F8H. Этот и шесть
следующих за ним адресов используются прерыванием BIOS INT 14H и
функцией _bios_serialcom() (вызывающей это прерывание).
Прямые манипуляции с этими адресами не очень сложны. Если,
например, мы установим в единицу седьмой бит порта с адресом
3FBH, а затем отправим младший байт задвижки делителя
скорости передачи (baud rate divisor latch) в порт 3F8H, а верхний
байт - в порт 3F9H, то сможем установить необходимую скорость
обмена данными с плоттером (или диджитайзером).
При передаче данных мы можем проверить первый бит порта
3FDH. Если он равен единице, то приемный буфер микросхемы
UART содержит некоторый символ. Опрашивая затем порт 3F8H
29
в цикле до появления XON, мы должны дождаться освобождения
приемного буфера. Анализируя остальные биты порта 3FDH, мы
можем определить тот момент, когда интерфейс будет свободен,
после чего - смело отправить необходимый символ в порт 3F8H.
Если вам нравится возиться с портами, адресами и битами,
попробуйте написать соответствующую функцию. Если не нравится
- попробуйте добиться того же эффекта, анализируя
возвращаемое функцией _bios_serialcom() значение.
ВОПРОСЫ И ЗАДАНИЯ
1. Измените текст функции dig_string() так, чтобы вместо самой
структуры POSITION в вызывающую функцию возвращался
указатель на нее. Измените соответствующим образом тексты
остальных функций, вызывающих dig_string(). Будет ли программа
работать после этого? Объясните почему. Проверьте результаты
для компилятора QuickC и основного оптимизирующего
компилятора Microsoft С.
2. В текстах вышеприведенных функций отсутствует контроль
реакции диджитайзера на ваши действия с ним. Воспользуйтесь
возвращаемым значением функции _bios_serialcom() для
введения такого контроля (расшифровка бит возвращаемого значения
приведена в предыдущем выпуске).
3. Адаптируйте тексты вышеприведенных функций для работы с
другими форматами данных.
4. Каким еще образом, кроме использования функции
_bios_serialcom() и прямого обращения по адресу в нижней
памяти, можно получить доступ к последовательному порту?
ЕЩЕ НЕМНОГО ОБ ОПЦИЯХ
КОМПИЛЯТОРА
В приложениях к первому и второму выпускам серии "С для PC"
были приведены полные списки опций драйверов CL и QCL
компилятора Microsoft С 5.0 с краткой расшифровкой их смысла.
Предназначение большинства этих опций понятно из таких
кратких описаний даже начинающим программистам, но некоторые
из них мне хотелось бы рассмотреть подробнее. Здесь я буду
придерживаться классификации опций по группам, использованной в
упомянутых -приложениях. Краткое их описание всегда можно
вывести на экран с помощью команд
CL/HELP
или
QCL/HELP
При этом опция /HELP может состоять из букв как нижнего, так
и верхнего регистров, а также из их смеси.
ГЕНЕРАЦИЯ КОДА
Ряд опций, управляющих генерацией кода, требует хотя бы
минимальных пояснений.
Опции /GO, /GJ, /G2.
Применение соответственно микропроцессоров 8086/8088,
80186/80188 и 80286,
Если вы пользуетесь микропроцессором 80286 (или 80386), вы
можете указать опцию /G2 - с тем чтобы установить для вашей
31
программы набор инструкций для микропроцессора 80286. По
умолчанию компилятор Microsoft С (и Microsoft QuickC)
использует набор инструкций для микропроцессора 8086/8088. На
компьютере с микропроцессором 80286 для большей эффективности
программ, написанных "для себя", разумеется, желательно
компилировать с опцией /G2. Впрочем, это не обязательно,
поскольку программы, скомпилированные с опцией /G2, не смогут
работать на машинах с микропроцессорами 8088 или 8086. Однако
программы, скомпилированные без опции /G2, либо программы,
скомпилированые с явно заданной опцией /G0, смогут работать и
на машинах с микропроцессорами 80186/80188, 80286 и 80386.
Опция /Gc.
Управление соглашениями о связях
Ключевые слова fortran, pascal, edeel вместе с опцией /Gc
управляют соглашениями о вызове и наименовании функций,
используемых вашими программами для того, чтобы они могли
вызывать или могли сами быть вызваны функциями, написанными на
языках Pascal и FORTRAN с использование средств фирмы
Microsoft.
Язык С, в отличие от Pascal и FORTRAN, позволяет
использовать функции с переменным числом аргументов, что
обусловливает другой порядок обработки вызовов функций. Pascal и
FORTRAN передают параметры функции в порядке их
следования слева направо, так что последний аргумент в списке
помещается в стек последним. Для С-функций порядок изменяется на
противоположный (справа налево), так чтобы первый аргумент в
списке помещался в стек последним.
Кроме того, обратным извлечением аргументов из стека в С
занимается вызывающая функция, в отличие от языков Pascal и
FORTRAN, где эту роль играет вызываемая функция. Если соот-'
ветствующий код является частью вызываемой функции (Pascal и
FORTRAN), то он включается в программу только один раз; если
же он принадлежит вызывающей функции, он включается в
программу столько раз, сколько в ней есть вызовов функций.
Поскольку число вызовов функций никак не меньше, а скорее
намного больше, чем число самих функций, способ вызова в языках
Pascal и FORTRAN более экономичен и эффективен.
32
Применение ключевых слов fortran и pascal (означающих, в
сущности, одно и то же - изменение порядка следования аргументов
в стек и извлечения их оттуда) позволяет видоизменить способ
вызова конкретной функции. Используя опцию компилятора /Gc,
вы измените этот способ для всего компилируемого модуля.
Для того чтобы сохранить первоначальный способ вызова
функции mainO, вам необходимо воспользоваться третьим ключевым
словом - edeel - или изменить процедуру начальной загрузки
программы (start-up routine). Для удобства все библиотечные
функции Microsoft С объявлены в соответствующих header-файлах с
использованием ключевого слова cdecl.
Использование ключевых слов Microsoft С, являющихся
расширениями языка (cdecl, far, fortran, huge, near и pascal), может быть
отменено с помощью опции компилятора /Za (выключить
расширения языка).
Опция /Gt[numbcrJ.
Установка границы данных
При использовании опции /Gt все элементы данных, размер
которых больше или равен number байт, будут размещаться в новом
сегменте данных. Если число number задано, оно должно
непосредственно следовать за опцией /Gt, без каких-либо
разделяющих пробелов. Если число number опущено, стандартное
пороговое значение составляет 256 байт. Если опция /Gt опущена,
стандартное пороговое значение - 32 767 байт.
Опцию /Gt можно использовать только для программы,
разработанной в компактной или большой модели памяти, поскольку
программы в малой или средней модели памяти имеют только
один сегмент данных.
Опция /Gs.
выключить проверни стека
Проверка стека означает, что при входе в процедуру стек сначала
проверяется на наличие в нем места для локальных переменных
данной процедуры. Если все нормально, пространство выделяется
перемещением указателя стека. В противном случае возникает
ошибка "stack overflow" (переполнение стека).
33
Если контроль стека подавляется, компилятор предполагает, что
места в стеке достаточно. Если же в действительности в стеке не
хватит места, вы можете перезаписать ячейки памяти в сегменте
данных и не получить предупреждения об этом.
Чаще всего контроль стека необходим только функциям,
предъявляющим очень большие требования к памяти для локальных
переменных (более 150 байт), так как между сегментами данных
и стеком достаточно свободного места для «обработки функций с
меньшими требованиями. При вызовах функции контроль стека
незначительно замедляет выполнение. Следующим библиотечным
функциям контроль стека необходим:
execvp fscanf sprintf sscanf
execvpe printf spawnvp system
fprintf scanf spawnvpe vprintf
ВЫХОДНЫЕ ФАЙЛЫ И ЛИСТИНГИ
Я не буду подробно рассматривать опции, связанные с созданием
листингов, а попробую пояснить смысл их существования на
небольшом примере.
Использование для компиляции программы WP, описанной в пер^
вом выпуске серии "С для PC", строки вида
cl /St "Which Path" /Ss WP /Fsexample wp.c /c
приведет к появлению текстового файла EXAMPLE.LST,
содержащего следующую любопытную информацию:
Which Path PAGE 1
WP 10-03-90
22:07:45
Line# Source Line Microsoft С Compiler Version 5.00
34
1 #include<stdio.h>
2 #include<stdlib.h>
3
4 main(int argc, char *argv[])
5 {
6 extern char *optarg;
7 extern int optind;
8 char option;
9 char varset = 0;
10 char *var;
11 char buffer[50];
12
13 while((option = getopt(argc, argv, "e:")) != EOF)
14 switch(option){
15 case !?' :
16 printf("\nCorrect usage: ");
17 printf("wp [/e env_var] fiIename\n");
18 exit(-1);
19 case 'e' :
20 var = optarg;
21 varset = 1;
22 break;
23 }
24
25 if(!argv[optind]){
26 printf("\nNo file specified\n");
27 exit(-2);
28 }
29
30 if(varset && !getenv(var)){
31 printf("\nVariable %s not set\n",var);
32 exit(-3);
33 }
34
35 if(lvarset)
36 var = "PATH";
37
38 _searchenv(argv[optind], var, buffer);
39
40 if(!buffer[0]){
35
41 printf("\nFile %s not found ",argv[optind]);
42 printf("in the List of %s\n",var);
43 }
44
45 eise
46 printf("\n%s\n",buffer);
47
48 }
main Local Symbols
Name Class Type Size Offset Register
buffer auto -0038
option auto -0006
Which Path PAGE 2
WP 10-03-90
22:07:45
Microsoft С Compiler Version 5.00
main Local Symbols
Name Class Type Size Offset Register
var auto -0004
varset auto -0002
argc param 0004
argv param 0006
optarg. . . . extern near pointer 2 ***
optind. . . . extern int 2 ***
Global Symbols
Namo " Class Type Size Offset
_soarchenv. . extern near function ***
cxil extern near function *** ***
gelonv. . . . extern near function ***
gotopt. . . . extern near function *** ***
36
main global near function *** 0000
printf. . . . extern near function *** ***
Code size = 0106 (262)
Data size = 008d (141)
Bss size = 0000 (0)
No errors detected
Как вы видите, текст программы представлен здесь в приятном
для чтения и анализа виде. Вместе с самим исходным текстом с
пронумерованными строками и упоминанием об ошибках (если
таковые имеются) в этом файле расположены три таблицы.
Первая из них располагается в конце каждой функции (здесь
только одна функция - ma in О) и содержит список локальных
переменных - auto, параметров функции - param и упомянутых
глобальных переменных - extern. В ней указывается также тип
переменной, ее размер и адрес отступа по отношению к
содержимому регистра ВР (положительная величина для param и
отрицательная - для auto). Для регистровых переменных в графе
Register также упоминается используемый регистр.
Вторая таблица располагается в конце исходного текста и
содержит информацию о глобальных символах - переменных и
функциях. Ее содержимое похоже на содержимое первой таблицы за
единственным очевидным исключением - отсутствием графы
"Register".
Третья таблица, расположенная в самом конце листинга,
содержит информацию об использованных сегментах. Число байтов в
каждом сегменте дано вначале в шестнадцатеричном, а.в
скобках - и в десятичном виде.
Я не буду приводить здесь все остальные листинги - это заняло
бы слишком много места. К тому же вы легко можете
воспроизвести их на своем компьютере. Попробуйте разобраться с ними
самостоятельно - это чрезвычайно полезно для понимания того,
что же именно делает компилятор с текстами ваших программ.
37
ПРЕПРОЦЕССОР
Драйверы CL/QCL имеют несколько опций, управляющих
действием препроцессора языка С. Рассмотрим две группы опций.
Опции III identifier, /и.
Удаление определений стандартно
предопределенаых идентификаторов
Компилятор Microsoft С определяет четыре идентификатора,
являющихся полезными при написании переносимых программ.
Данными идентификаторами следует пользоваться для
компиляции частей программы, зависящих от используемого процессора,
операционной системы и мЪдели памяти. Приведем стандартно
определенные идентификаторы и их значения:
Идентификатор
Функция
MSDOS
Всегда определен. Идентифицирует
операционную систему, как MS-DOS
Всегда определен. Идентифицирует
компьютер, как относящийся к семейству
микропроцессоров Intel 80п86
Всегда определен. Задает модель памяти,
где m - это либо S (малая модель), С
(компактная модель), М (средняя модель), L
(большая модель) или Н (огромная модель)
Определяется только в случае, если
языковые расширения не разрешены (то есть если
программы компилируются с опцией
компилятора /Za либо с выключенной опцией
Language Extensions в среде QuickC)
Оптимизирующий компилятор Microsoft С 5.0 определяет еще
один идентификатор - _CHAR_UNSIGNED, который
определяется только тогда, когда задана опция /J, определяющая тип char
по умолчанию как не имеющий знака - usigned.
Опция /U (отменить определение) выключает определение
данного стандартно заданного идентификатора. В одной командной
М 186
М 186т М
NO EXT KEYS
38
строке можно задать более одной опции /U. Опция /и выключает
все определения, кроме M_I86mM.
Удалить определения стандартных идентификаторов вам может
понадобиться в следующих случаях:
1. Если вы хотите задать в командной строке более чем
максимально разрешенное число определений. Если языковые
расширения отменены, вы можете задать 16 определений, и 17
определений - в противном случае.
2. Если вы хотите, чтобы в вашей программе стандартный
идентификатор обозначал что-нибудь другое.
Взамен каждого удаленного стандартного идентификатора вы
можете в командной строке подставить определение своего
собственного. Если вы удалите определения всех четырех стандартных
идентификаторов, вы можете ввести до двадцати определений с
командной строки. Однако, поскольку DOS ограничивает
количество символов, которые можно задать в командной строке,
количество определений, которые вы можете задать, на практике
оказывается значительно меньше, нежели 20. Компилятор также
определяет два дополнительных идентификатора, указывающие на
тип микропроцессора системы:
М_18086 Идентифицирует микропроцессор
компьютера как Intel 8086 (эквивалентен опции /GO)
М_1286 Идентифицирует микропроцессор
компьютера как Intel 80286 (эквивалентен опции /G2)
Опции IP, IE, /EP.
Вывод листинга препроцессора
Опции /Р,/Е и /ЕР выводят листинг файла после обработки
препроцессором. Использование данных опций позволяет вам
проверить вывод препроцессора языка С. Все три опции подавляют
компиляцию; не производится ни объектного файла, ни карты
распределения памяти, даже если в командной CL/QCL'строке будут
заданы опции /Fo или /Fm.
Листинг препроцессора идентичен оригинальному исходному
файлу, за исключением того, что все директивы препроцессора
39
выполняются, расширяются все макрокоманды, а комментарии
удаляются. Вывод препроцессора записывается в файл с тем же
самым именем, что и исходный файл, но с расширением Л.
Наиболее своеобразная опция в этой группе - /Е помещает
директивы #line в начало и конец каждого включаемого файла и
вокруг строк, удаленных директивами препроцессора, задающими'
условную компиляцию. Опция полезна, если вы перед
компиляцией хотите просмотреть листинг препроцессора. Директивы
#line нумеруют строки файла, полученного после работы
препроцессора, поэтому ошибки, сгенерированные на более поздних
стадиях обработки, относятся к оригинальному исходному файлу, а
не к файлу, полученному после обработки препроцессором.
ЯЗЫК И ОТЛАДКА
Опции /Zi, /Zd.
Подготовка к отладке
Опция /Zi производит объектный файл, содержащий информацию
для отладки в символических адресах, - для использования
встроенным отладчиком в среде QuickC либо символическим
отладчиком Code View. Данный объектный файл содержит полную
таблицу символических адресов и номера строк.
Опция /Zd производит объектный файл, содержащий номера
строк, соответствующие номерам строк исходного файла. Опция
/Zd полезна в тех случаях, когда вы хотите уменьшить размер
выполняемого файла, который вы будете отлаживать с помощью
отладчика Code View, и вам в процессе отладки не понадобится
использовать вычисление выражений.
Опция /ZL
Подавление выбора стандартной библиотеки
Обычно компилятор помещает в объектный файл имя
стандартной библиотеки, соответствующей выбранной модели памяти и
пакету математических операций. Данный механизм позволяет
компоновщику автоматически находить соответствующую
библиотеку. При заданной опции /Z1 компилятор не будет помещать в
объектный файл имя стандартной библиотеки. В результате объе-
40
ктный файл будет несколько меньшего размера. Опция /Z1
бывает полезна, когда вы используете утилиту LIB для построения
автономной библиотеки.
Вы можете скомпилировать объектные файлы, помещаемые вами
в библиотеку, с опцией /Z1, для того, чтобы удалить имена
библиотек перед их объединением. Кроме того, опция /Z1 сэкономит
небольшое количество памяти для каждого объектного файла,
значительно большее количество памяти будет в сумме
сэкономлено в библиотеке, содержащей много объектных модулей.
Опция /Zpffl\2\4}J.
Упаковка членов структуры
При размещении структуры в памяти ее члены обычно хранятся
следующим образом:
- Элементы типа char или unsigned char либо массивы,
содержащие элементы данных типов, выравниваются на границу байта.
- Структуры выравниваются на границу слова; структуры
нечетного размера дополняются до четного количества байтов.
- Остальные типы членов структур выравниваются на границу
слова.
Для экономии памяти может понадобиться хранить структуры в
более компактном виде. Опция /Zp и директива pragma pack
управляют "упаковкой" структур в памяти.
#pragma pack ([{1|2|4}])
На некоторых процессорах, применение /Zp может замедлить
выполнение программ, поскольку в этом случае требуется некоторое
время для доступа к структурам и их распаковки. Например, на
микропроцессоре 8086 данная опция может снизить
эффективность, если члены структур типов int или long упаковываются
таким способом, что они начинаются на границе нечетного байта.
Пользуйтесь опцией /Zp, чтобы определить аналогичную
упаковку для всех структур в модуле. Если вы зададите опцию /Zp[n],
где п - это 1, 2 или 4, каждый член структуры после первого
М Ьак.З*Ю
хранится на границе п-го байта в зависимости от выбранной вами
опции. Если вы используете опцию /Zp без аргумента, члены
структур упаковываются на 1-байтовой границе.
Используйте директиву pragma pack, если вы хотите определить
упаковку иную, чем упаковка, определенная в командной строке
для определенных структур. Задайте pragma pack(n) где n - это 1,
2, или 4, перед описанием тех структур, которые вы хотите
упаковать иным способом. Чтобы восстановить упаковку, заданную в
командной строке, задайте pragma packO без аргументов.
Покажем взаимодействие опции /Zp и директивы pragma pack:
Аргумент Компиляция Действие
директивы с опцией /Zp ?
pragma pack
О Да Возвращает к упаковке,
заданной в командной строке
для следующих за ней
структур
О Нет Возвращает к стандартной
упаковке для следующих за
ней структур
(п) Да или нет Упаковывает следующие
далее структуры по заданной п-
байтовой границе до тех пор,
пока значение аргумента не
будет изменено (отключено)
ОПЕРАЦИИ С ПЛАВАЮЩЕЙ ТОЧКОЙ
Драйверы CL/QCL имеют ряд опций, позволяющих вам выбрать
способ, каким компилируемая программа будет обрабатывать
математические операции с плавающей точкой. Эти опции
начинаются с буквосочетания /FP и состоят из двух частей.
Первая часть указывает вид, в котором математические операции
с плавающей точкой включаются в программу; это может быть
42
генерация либо инлайновых инструкций, либо вызовов
библиотечных функций для математических операций. Первому случаю
соответствует буква i в названии опции (in-line), второму - буква
с или a (call). В первом случае программы будут исполняться
быстрее (особенно при наличии сопроцессора), но при этом
окажется невозможным использовать библиотеку Alternate; во втором
случае можно использовать при компоновке любую библиотеку
для математических операций с плавающей точкой.
Вторая часть указывает, какая именно из библиотек для
математических операций с плавающей точкой будет использована по
умолчанию при компоновке. Цифры 87 обозначают
использование библиотеки сопроцессора, а пустое место указывает на
использование по умолчанию библиотеки эмулятора.
Поясним результаты использования этих опций на трех примерах
(остальные две опции расшифровываются аналогичным образом).
Опция /FPL
Эмулятор (плюс инлайновые
инструкции сопроцессору 8087/80287)
Обрабатывает операции с плавающей точкой с помощью
программного обеспечения, эмулирующего работу математического
сопроцессора 8087 или 80287. Чтобы выполнять программы с
данной опцией, вам не нужен реальный математический
сопроцессор, хотя такая программа будет успешно работать и на системах
с сопроцессором. Данная опция используется по умолчанию.
Опция /FPi87.
Инлайновые инструкции сопроцессору 8087/80287
Обрабатывает математические операции с плавающей точкой с
помощью генерации инструкций для математического
сопроцессора 8087 или 80287. Использование данной опции сокращает
размер программы, но программа всегда будет работать только на
системах, имеющих сопроцессор. Если же сопроцессор не
установлен, то исполнение программы прервется и на экран будет
выведено следующее сообщение:
run-time error R6002 - floating point not loaded
H
43
Опция IFPa.
Генерация вызовов функций библиотеки Alternate
Обрабатывает математические операции с плавающей точкой с
помощью генерации вызовов функций библиотеки Alternate.
Математический сопроцессор 8087/80287 игнорируется.
Использование данной опции сокращает размер программы и скорость
исполнения программы (на системе без эмулятора), но при этом в
жертву приносится точность вычислений.
РАЗНОЕ •
Здесь дается пояснение некоторым опциям общего назначения.
Опция /Н <число>.
Максимальная длина имени внешней ссылки
Компилятор Microsoft С не накладывает ограничений на длину
имени внешней ссылки (хотя и использует только первые 31
символ). Тем не менее в целях совместимости с другими
компиляторами и компоновщиками и соответственно для лучшей
переносимости написанных вами программ полезно бывает ограничить
максимальную длину имени внешней ссылки. При этом лишние
символы в именах переменных будут попросту отброшены.
Опция /V <строка>.
Вставить текстовую строку в OBJ-файл
Если вы хотите поместить в OBJ или ЕХЕ-файл какое-либо
текстовое сообщение или просто свое имя, которое легко можно будет
прочесть в чертовщине машинного кода, воспользуйтесь этой
опцией. Не забудьте окружить кавычками внедряемую строку, если
она содержит пробельные символы или сами кавычки (").
Опция /Тс<имя файла>.
Компилировать файл без расширения .С
Компилятор идентифицирует исходные файлы по наличию
расширения .С. Файлы с любыми другими расширениями или без
расширения рассматриваются как объектные и передаются
компоновщику. Во избежание недоразумения воспользуйтесь данной
44
ПРИЛОЖЕНИЕ А
ИСПОЛЬЗОВАНИЕ ОПЦИЙ КОМПОНОВЩИКА LINK
Все опции компоновщика начинаются со знака опции (/). В
опциях компоновщика буквенный регистр не играет существенной
роли, например /NOI и /noi эквивалентны.
Чтобы сократить место в командной строке и вашу работу,
сокращайте опции компоновщика. Однако удостоверьтесь, что ваше
сокращение уникально и компоновщик сможет распознать, какую
опцию вы имеете в виду. (Минимальная аббревиатура каждой
опции определяется ее синтаксисом.)
Например, некоторые опции начинаются с буквенного сочетания
"NO"; таким образом, чтобы быть уникальной, аббревиатура
данной опции должна быть длиннее, чем "NO". Вам нельзя
использовать сокращение "NO" для опции "/NOIGNORECASE",
поскольку компилятор не сможет распознать, какую из( опций,
начинающихся на "NO", вы имеете в виду. Кратчайшая корректная
аббревиатура для данной опций будет "/N01".
Аббревиатура должна начинаться с первой буквы опции и должна
продолжаться до последней введенной буквы. Никакие искажения
и пропуски не допускаются.
Некоторые из опций компоновщика имеют числовые аргументы.
Числовой аргумент может быть одним из следующих:
- Десятичное число в пределах от 0 до 65 535 (обратите
внимание, что запятые не должны стоять в числовых аргументах).
- Восьмеричное число от 0 до 0177777. Число интерпретируется,
как восьмеричное, если оно начинается с "О". Например, число
10 - это десятичное число, а 010 - восьмеричное число,
эквивалентное 8 в десятичной системе.
- Шестнадцатеричное число в пределах от 0 до OxFFFF. Число
интерпретируется как шестнадцатеричное, если оно начинается с
45
"Ох" или "ОХ". Например, OxlO - это шестнадцатеричное число,
эквивалентное 16 в десятичной системе.
Опции компоновщика влияют на все файлы, участвующие в
процессе редактирования связей, независимо от того, где рни заданы.
ОПЦИИ ПРОГРАММЫ LINK, ОБЩИЕ ДЛЯ QuickC
И Microsoft С Optimizing Compiler
Опция /HEfLPJ.
Просмотр списка опций
Используется для вывода на экран списка опций компоновщика.
LINK /HE
Microsoft (R) Overlay Linker Version 3.61
Copyright (С) Microsoft Corp 1983-1987. All rights reserved.
Valid options are:
/BATCH /CODEVIEW
/CPARMAXALLOC /00SSEG
/OSALLOCATE /EXEPACK
/FARCALLTRANSLATION /HELP
/HIGH /INFORMATION
/LINENUMBERS /MAP
/NODEFAULTLIBRARYSEARCH /NOEXTOICTIONARY
/NOFARCALLTRANSLATION /NOGROUPASSOCIATION
/NOIGNORECASE /N0PACKC00E
/OVERLAYINTERRUPT /PACKCOOE
/PAUSE /QUICKLIBRARY
/SEGMENTS /STACK
Опция /PAUfSEj.
Пауза в процессе компоновки
/PAU вынуждает компоновщик остановить работу перед
созданием на диске выполняемого файла (.ЕХЕ) и выдать сообщение.
46
Это позволяет установить новую дискету, чтобы записать
выполняемый файл. Если задать опцию /PAU, программа LINK перед
созданием выходного файла выдаст следующее сообщение:
About to generate .EXE file
Change diskette in drive letter and press <ENTER>
(Предстоит генерация файла .ЕХЕ
Смените дискету в дисководе letter и нажмите <ENTER>)
letter - спецификация текущего дисковода. Программа LINK
продолжит выполнение после нажатия клавиши ENTER.
Эта опция наиболее полезна в том случае, когда вы используете •
пакет QuickC, установленный на гибкие диски. Если это так,
примите к сведению еще ряд замечаний, связанных с опцией
/PAU. Не удаляйте диск, содержащий заново созданный файл
карты распределения памяти, либо диск, используемый для
временного файла. Если временный файл создается на диске, который
вы планируете сменить, нажмите CTRL+C, чтобы закончить
сеанс компоновки. Реорганизуйте свои файлы таким образом,
чтобы временный файл и выполняемый файл могли быть записаны
на один и тот же диск. Затем возобновите процесс компоновки.
Опция I INFORMATION].
Вывод на экран информации о процессе компоновки
Опция может быть полезной, если вы хотите определить
местоположение связываемых объектных файлов и порядок, в котором
они компонуются. При использовании данной опции на экран
выводится информация о процессе компоновки, включая каждую
фазу компоновки и имена связываемых объектных файлов.
Приведимый ниже пример показывает сеанс работы
компоновщика при заданной опции /I.
**** PASS ONE ****
NTOI.OBJ(htoi.c)
**** LIBRARY SEARCH ****
c:\lib\MLIBCE.LIB(chkstk)
47
c:\lib\MLIBCE.LIB(crto)
c:\lib\MLIBCE.LIB(printf)
c:\libMLIBCE.LIB(scanf)
**** ASSIGN ADDRESSES ****
**** PASS TWO ****
HTOI.OBJ(htoi.c)
c:\lib\MLIBCE.LIB(chkstk)
c:\lib\MLIBCE.LIB(crto)
c:\lib\MLIBCE.LIB(printf)
c:\lib\MLIBCE.LIB(scanf)
**** WRITING EXECUTABLE ****
Segments 25
Groups 1
Bytes in symbol table 16400
Опция /BfATCHJ.
Запрет на запросы компоновщика
Опция /В запрещает компоновщику запрашивать у пользователя
новый маршрут, если он не может найти требуемую библиотеку
либо требуемые объектные файлы.
Если используется данная опция, компоновщик просто
продолжает выполнение без запрашиваемого файла.
Использование опции /В может привести к неразрешенным
внешним ссылкам (unresolved externals). Данную опцию следует
использовать в основном тем, кто использует пакетную обработку
либо утилиту МАКЕ и, связывая много файлов в одной команде,
не хочет прерывать процесс компоновки из-за одного
ненайденного файла. Опция, однако, не запрещает компоновщику
запрашивать аргументы, не заданные в командной строке LINK.
Опция /QfUICKLIBJ.
Создание библиотек типа Quick library
Опция /Q сообщает компилятору, что следует объединить
заданные объектные файлы и библиотеки в библиотеку типа Quick
library, которую можно использовать для работы с Microsoft
48
QuickBASIC или Microsoft QuickC. Библиотеки типа Quick library
имеют расширение .QLB, а не .ЕХЕ для того, чтобы отличить их
от выполняемых файлов. Когда вы вызываете QuickC, в
командной строке QC вы можете задать опцию /1 и загрузить таким
образом требуемую библиотеку типа Quick library.
При создании библиотеки типа Quick library имя файла
QUICKLIB.OBJ должно быть первым в списке имен объектных
файлов для компоновки. Подробную информацию о создании и
загрузке библиотек типа Quick library вы найдете в последующих
выпусках серии "С для PC".
Опция /EfXEPAGKJ.
Упаковка исполняемых файлов
Опция /Е перед созданием выполняемого файла выбрасывает из
него последовательности повторяющихся байтов (обычно нулевые
символы) и оптимизирует загрузочную таблицу размещения
(load-time relocation table).
Таблица размещения - это таблица ссылок относительно начала
программы. Когда выполняемый образ программы загружается в
память, каждая ссылка изменяется и каждой точке входа
присваивается действительный адрес.
Выполняемые файлы, скомпонованные с данной опцией, могут
быть меньше по размеру и загружаться быстрее, чем файлы,
полученные без данной опции. Однако вы не сможете отлаживать
упакованные программы с помощью многооконного отладчика
Microsoft CodeView либо с помощью отладчика встроенного, в
программную среду QuickC.
Опция /NOD[EFAULTLIBRARYSEARCH].
Игнорирование стандартных библиотек
Требует, чтобы компоновщик не искал для разрешения внешних
ссылок ни одной библиотеки, заданной в объектном файле.
Как правило, задавать компоновщику имя автономной
библиотеки не требуется. Когда команда CL/QCL создает объектные
файлы, она помещает в каждый объектный файл имя подходящей
для него автономной библиотеки. CL/QCL определяет имя соот-
49
встствующей библиотеки по опциям модели памяти и плавающей
точки, заданных в командной строке.
Обычно компилятор Microsoft С не может корректно работать без
автономных библиотек, построенных с помощью программы
SETUP. Таким образом, при использовании опции /NOD вам
следует явно задать имя требуемой автономной библиотеки.
Опция /SEfGMENTS J .-number.
Установка максимального числа сегментов
Опция /SE управляет числом сегментов, которые компоновщик
позволяет иметь программе. Стандартное число сегментов 128, но
вы можете задать свое число number (десятичное, восьмеричное,
шестнадцатеричное) в пределах от 1 до 3072 (десятичное).
Для каждого сегмента компоновщик должен выделить место для
хранения сегментной информации. Если вы зададите число
сегментов, большее 128, компоновщик соответственно отведет для
сегментной информации больше места. Для программ с количеством
сегментов меньшим, чем 128, вы можете уменьшить количество
памяти, требуемое компоновшику, задав число number, равное
действительному количеству сегментов в программе.
Компоновщик выдаст сообщение об ошибке
segment Iimit too high
если данное число слишком велико для объема памяти,
предоставленной компоновщику.
Опция ICP[ ARM AX ALLOC]-.number.
Установка максимального количества отводимой памяти
Опция /СР установливает максимальное число 16-байтовых
параграфов, требуемых для программы при загрузке в оперативную
память, равным величине number (целое число от 1 до 65535).
Операционная система использует данное значение для
распределения памяти при загрузке программы. Стартовый модуль
Microsoft С усекает память соответственно большему из
следующих двух значений:
50
- Самый большой блок непрерывной памяти, доступной DOS.
- Количество памяти, заданное с помощью опции /СР.
Для программ с ограниченным количеством данных типа static и
динамическим распределением памяти данная опция не является
необходимой.
Опция lM[AP][:number].
Создание файла карты распределения памяти со списком
глобальных символических имен (listing public symbols)
Опция /М создает файл карты распределения памяти. Файл
распределения памяти содержит список сегментов программы в
порядке их появления в загруженном модуле. Пример такого файла
приведен ниже.
Start Stop Length Name Class
OOOOOH 01E9FH 01EAOH _TEXT CODE
01EAOH 01EAOH OOOOOH C_ETEXT ENDCODE
Информация в колонках Start и Stop - 20-разрядный адрес (в ше-
стнадцатеричной системе) каждого сегмента относительно начала
загрузочного модуля. Загрузочный модуль начинается с адреса 0.
Колонка Length - длина сегмента в байтах.
Колонка Name - имя сегмента.
Колонка Class - информация о типе сегмента.
После списка сегментов появляется информация о группах:
стартовый адрес и имена групп. Приведем пример списка группы
(DGROUP - имя группы данных):
Origin Group
01ЕА:0 DGROUP
Если вы выполняете компоновку с помощью опции /М, файл
распределения памяти содержит два списка глобальных
символических имен: первый отсортирован по именам (по возрастанию
символов ASCII), а второй - по возрастанию адресов. Примечание
Abs появляется рядом с абсолютными символическими именами.
(Это имена, содержащие 16-разрядные константы, значения
которых не связаны с программными адресами.)
51
Address Publics by Name
01ЕАЮ096 STKHQQ
0000:1D86 _brikctl
01EA:04B0 _cdala
01ЕАЮ910 _end
01ЕАЮ0ЕС _abrkp
01EA:009C _abrktb
01ЕАЮ0ЕС _abrktbe
0000:9876 Abs_acrtmsq
0000:9876 Abs acrtused
01ЕАЮ240
01ЕАЮ242
Address
0000:0010
0000:0047
0000:00DA
0000:0113
0000:0129
0000:01C5
argc
argv
Publics by Value
main
_htoi
_expl6
jhkstk
astart
_cintDIV
Каждый список может содержать максимально 2048
отсортированных символических имен. В опции /М можно задать число
number, если вы хотите выдать список из большего числа
символических имен, отсортированных по адресам. Аргумент number
может быть любым - десятичным, восьмеричным либо шестнадца-
теричным числом от 0 до 65535 (десятичное) включительно;
однако на практике число отсортированных символических имен
ограничено количеством ближней динамической области памяти.
Если задать аргумент number, в файле распределения памяти
будет отсутствовать список "Publics by Name" (внешние
символические имена, отсортированные по возрастанию символов ASCII).
Большинство глобальных символических имен, появляющихся в
файле карты распределения памяти, являются символическими
именами, внутренними для компилятора Microsoft С. Данные
символические имена обычно начинаются с одного или более лиди-
52
рующих подчеркиваний и заканчиваются на QQ. Адреса внешних
имен записываются в формате фреймхмещение, показывающем
местоположение имени относительно начала загрузочного модуля.
Опция /М включает в файл распределения памяти точку входа в
программу, как показано в следующем примере.
i
Program entry point at 0000.0129
Файл распределения памяти можно создать, задав опцию /Fm в
командной строке CL/QCL, заданием имени такого файла в
командной строке LINK либо заданием имени данного файла в
ответе на запрос List File. Но эти методы не позволят увеличить
количество символических имен, отсортированных по адресам.
Опция /LIfNENUMBERSJ.
Создание файла карты распределения памяти с включенными
номерами строк программы (including line numbers in map file)
Опция /LI создает файл карты распределения памяти, который
содержит список сегментов программы в порядке их появления в
загруженном модуле. Пример такого файла и значения его
отдельных полей приведены в предыдущем разделе для опции /М.
Если вы производите компоновку с помощью опции /LI, файл
карты распределения памяти содержит также номера строк вашей
исходной программы и адреса, соответствующие каждому номеру
строки, как показано в приведенном ниже примере.
Line number for HTOLOBJ(hloi.c) segment _TEXT
2 0000:0020 4 0000:0019 5 0000:0023 6 0000:0031
7
14
19
23
29
36
43
0000:0047
0000:005F
0000:007D
0000:0095
0000:00C2
0000-.00D6
0000:01 OF
10
16
20
25
30
38
0000:004В
0000:0067
0000:008 A
0000:009A
0000:00C5
0000-.00DF
12
17
21
26
32
40
0000:0054
0000:0070
0000:008D
0000:009D
0000:00CA
0000-.00E9
13
18
22
28
33
42
0000:005A
0000:0078
0000:008F
0000:00A5
0000:00D2
0000:0107
Опция /LI включает в файл распределения памяти точку входа в
программу, как показано в предыдущем разделе.
53
Опция IST[ACK]:number.
Управление размером стека
Опция /ST определяет размер стека программы, где number - это
любое положительное десятичное, восьмеричное или шестнадца-
теричное значение до 65535 (десятичное), являющееся размером
стека в байтах. Восьмеричные и шестнадцатеричные значения
следует задавать в формате, принятом в языке С: восьмеричные
числа начинаются с нуля, а шестнадцатеричные - с "Ох".
Если не задать данную опцию, стартовая программа из
автономной С-библиотеки установит стандартный размер стека в 2
килобайта. Если получено сообщение о переполнении стека, следует
увеличить его. И наоборот, если программа использует очень
маленький стек, можно уменьшить его размер.
Данная опция имеет то же самое действие, что и опция /F в
командной строке CL/QCL.
Опция /FfARCALLTRANSLATIONJ.
Оптимизация "дальних" вызовов
Опция /F компоновщика просит компоновщик оптимизировать
"дальние" вызовы процедур, лежащих в том же самом сегменте,
что и вызывающая. Данная опция, будучи использованной вместе
с опцией /РАС, может приводить к созданию значительно более
быстрых и компактных программ.
При задании опции /F компоновщик оптимизирует 32-битовые
"дальние" вызовы (в средней и большой моделях памяти)
процедур, лежащих в том же сегменте, что и вызывающая процедура.
Поскольку адрес сегмента для вызванных и вызывающих
процедур один и тот же, то требуется 16-битовый "ближний" вызов.
Если задана опция /F, компоновщик удаляет "дальние" вызовы
и заменяет их на код, помещающий в стек содержимое регистра
CS, а затем делает "ближний" вызов. Вызванная процедура
возвращается с "дальней" (32-битовой) инструкцией возврата.
Поскольку сегмент кода, хранящийся в CS, и "ближний" адрес
находятся в стеке, "дальний" возврат происходит корректно.
Компоновщик также добавляет инструкцию пор, так что пятибайтовый
"дальний" вызов заменяется пятью байтами. В терминах
ассемблерных инструкций /F заменяет строку "дальнего" вызова
54
call FAR label
на следующую последовательность
push cs
call NEAR label
nop
Опция /F не влияет на эффективность программ, использующих
только "ближние" вызовы (то есть, созданных в малой и
компактной моделях памяти).
Опция /NOFfARCALLTRANSLATIONJ.
Отмена оптимизации "дальних" вызовов
Хотя компоновщик не использует преобразования "дальних"
вызовов, описанные в предыдущем разделе, если вы его явно не
попросите об этом, вы можете применить опцию /NOF для
выключения трансляции "дальних" вызовов, если вы, например, задали
опцию компоновщика /F в переменной окружения CL.
Опция /PAC[KCODE][:number].
Упаковка последовательных сегментов
Опция /РАС просит компоновщик сгруппировать соседние
сегменты кода. Сегменты кода в одной и той же группе разделяют
один и тот же самый адрес сегмента; все адреса смещений, если
требуется, регулируются по верхнему. Другими словами, все
элементы должны иметь корректный физический адрес независимо
от того, используется опция /РАС или нет. В результате действия
опции /РАС многие инструкции, которые могли бы в противном
случае иметь различные сегментные адреса, будут иметь один и
тот же сегментный адрес.
Используемая совместно с опцией компоновщика /F опция /РАС
может сократить размер и улучшить эффективность программ,
созданных в средней и большой моделях памяти.
Число number указывает предельный размер групп, формируемых
с помощью /РАС. Компоновщик перестает добавлять сегменты к
55
определенной группе, как только добавление следующего
сегмента превысит предельный размер группы. В данном случае
компоновщик из оставщихся сегментов формирует новую группу. Если
number не задано явно, его значение по умолчанию - 65530.
Опция /NOPfACKCODEJ.
Отмена упаковки последовательных сегментов
Хотя компоновщик не упаковывает соседние сегменты, если вы
не попросите его об этом явно, вы можете применить опцию
/NOP для выключения упаковки сегментов, если вы, например,
задали опцию /РАС в переменной окружения CL.
Опция INOEfXTDICTIONARY].
Отмена расширенного библиотечного словаря
Опция /NOE указывает компоновщику не использовать
расширенный словарь при поиске в библиотеках. Расширенный
словарь - это блок библиотечных данных, сообщающий
компоновщику о внутрибиблиотечных зависимостях.
Используйте эту опцию, если вы переопределяете процедуру
стандартной библиотеки (например, _setargv или _nullcheck) в своей
программе или вам мешает ошибка компоновщика L2044. Эта
опция может замедлить процесс компоновки.
ОПЦИИ ПРОГРАММЫ LINK,
НЕ ИСПОЛЬЗУЕМЫЕ QuickC
Не все опции команды LINK подходят для использования
программами QuickC Опции, перечисленные ниже, могут быть
использованы для программ Microsoft С, но для QuickC они обычно
никогда не требуются, так как требуемые действия драйвер QCL
либо компилятор Microsoft QuickC выполняет автоматически.
Опция /COfDEVIEWJ.
Подготовка к отладке
Готовит выполняемый файл к использованию его отладчиком
CodeView или к отладке в QuickC. Опция имеет смысл, если
компонуются объектные файлы, скомпилированные с опцией /Zi.
56
Опция /NOIfGNORECASEJ.
Различие между буквами верхнего и нижнего регистра
Опция /NOI просит компилятор делать различие между буквами
верхнего и нижнего регистра. Например, компоновщик будет
считать ABC и Abe различными именами. По умолчанию
компоновщик LINK рассматривает их как одинаковые имена, что критично
для С-программ, в отличие от некоторых других языков уровня.
Если компоновка осуществляется без опции /NOI, то следует
выполнить ее отдельным шагом - с помощью команды LINK.
■Опция /DOfSSEG].
Упорядочение сегментов
При использовании опции /DO для сегментов будет соблюдаться
порядок, принятый по умолчанию для высокоуровневых
языковых продуктов фирмы Microsoft.
1. Вначале располагаются все сегменты с именем класса,
оканчивающимся на CODE.
2. Далее находятся все остальные сегменты, не принадлежащие
группе DGROUP.
3. Затем расположены сегменты DGROUP:
BEGDATA;
все остальные, кроме BEGDATA, BSS и STACK;
BSS и STACK.
ПРОЧИЕ ОПЦИИ ПРОГРАММЫ LINK
Следующие опции не нужно применять при компоновке
объектных файлов, скомпилированных компиляторами Microsoft С и
QuickC. Они подходят только для объектных файлов,
скомпилированных посредством Макроассемблера Microsoft (MASM). В
связи с этим они кратко описаны для справки.
Опция /DS[ALLOCATEJ.
Управление загрузкой данных
По умолчанию компоновщик LINK загружает все данные
начиная с младших адресов стандартного сегмента данных. При ис-
57
пользовании опции /DS LINK загружает все данные начиная со
старших адресов стандартного сегмента данных.
/DS обычно используется вместе с опцией /HI, описанной ниже.
Опция /HlfGHJ.
Управление загрузкой ЕХЕ-файла
По умолчанию компоновщик LINK помещает выполняемый файл
в память по возможно более младшим адресам. При
использовании опции /HI LINK помещает выполняемый файл в память по
возможно более старшим адресам.
Опция /NOGfROUPASSOCIATIONJ.
Игнорирование групповых связей
/NOG просит компоновщик игнорировать групповые связи при
назначении адресов элементам кода и данных. Она сохранена для
совместимости с предыдущими версиями компоновщика (2.02 и
ранее) и компиляторов для языков фирмы Microsoft.
Опция/Of VERLAYINTERRUPTJ.number.
Задание номера оверлейного прерывания
Компоновщик передает вызовы из корня в оверлей и вызовы из
одного оверлея в другой оверлей с помощью прерывания (за
которым следует идентификатор модуля и смещение). По умолчанию
номер прерывания равен 63 (3F в шестнадцатеричной системе).
Для изменения номера прерывания вы можете воспользоваться
опцией компоновщика /О.
Опция /О определяет номер прерывания для передачи
управления оверлеям как number. Число number может принимать любое
положительное десятичное, восьмеричное либо шестнадцатерич-
ное значение до 255 (десятичное).
Опция /О должна применяться только в программах,
использующих оверлейные структуры и порождающих другие программы,
использующие оверлейные структуры. В данном случае каждая
программа должна использовать отдельный номер оверлейного
прерывания, откуда следует, что хотя бы одна программа должна
быть скомпонована с данной опцией.
58
19-
...ТТ Л д О ЕГ
4 *>s* i
p x pa
• IS I
"> 8 i .
jj Jo
s go § в So
о х Э о д s3 о {г
BS--xs5SH
О и.
z с 8
„•ело н,
18
S §Й2ЧЯ о 8-Е8Й
SI■Mil
X ~ X & Ц OS-1 5 В Ь
с ют? о s О Ч<
О. К В<й
о>
il^f!fl"iU£S№u!»
illiillilil!5^§iiii
i!«g§5~sg
x3§to3o||
?o x
^ с я: »^7
к Ms
- о у S и
1 lilf in
15 *:
59Щ1Ш
£ S и >> 5 Я х Р.5. rt £ В fc 8>s a J; ~.s « &
:вЖ|1.
|fil|r^llil§a?piii|§tiii!lipia
I
GNUbh MS-DOS (перенос 17 программ GNU в MS-DOS Торстена Ола) ,. $50
OBJASM (OBJ-файлы в ассемблер; MASM-совместимый вывод) $50
YATE (Yet AnotherText Editor - еще один текстовый редактор; на C++, полноэкранный) $50
ХШ Class Ubrary (основные классы C++ и книге «Date Abstraction and Object-Oriented Programming in C++» К.Горлена) $50
Editor Pack (15 текстовых редакторов; microEmacs 3.11, Stevie, Elvis, Moke, mg2a, DTE, Jove, Origami и т.д.) $50
Bison и BYACX (YACC-подобные программы; руководство; без ограничений на вывод BYACC) $35
PC-XINU (операционная система ХЮШ для PC) : - $35
Spell Pack (6 спеллчекеров, 2 набора утилит и 60К словарь: ispell, Mlcrosp, Sp, Cspella, Spell, Dawg, Soundex) $30
GNUAwkMDiffflnaPC (обе программы вместе) Г. $30
Make Pack (8 версий Make: dmake 3.7, GNU Make, Cake, и Gymake; генератор Make-файлов) $30
New! Big Number Pack (6 арифм. пакетов произвольной точности на С, один на Фортране с С-конвертором) $30
Crunch Pack (30 программ сжатия/восстановления файлов) $30
New! GNU Ghostscript (Turbo С Make-файл для MS-DOS, драйвер и шрифты для PaintJet) $25
String Pack (масса строковых функций; класс строк C++, BAWK, Воуег-Мооге, гипертекст и т.д.) $25
PERL for MS-DOS (С, sed, awk, и shell в едином языке; руководство) $25
FLEX (новый, улучшенный LEX; BSD версия 2.3.6 с руководством) $25
LISP 2.1 (с улучшениями Almy) $20
SDBM (быстрая утилита управления большими «хэш»-таблицами) _ $20
X-Wmdows (клиент-сервер, Xlib, исполняемые, XAWh т.д.; текстовые различия по XI1R4; TCP или подобные) $200
Kyoto Common Lisp (вкус Остина, естественно, версия 1.530; GCC и Portable Common Loops (PCD) $140
GNU Emacs версия 18.55 (полный исходный текст и исполняемые модули) $125
Waffle BBS (интерфейс с UUCP и В или С news; конференции; файловая секция; полная подвды-.^а tSL.NET) $120
GNU С Compiler (gcc) версия 1.39 (полный исходный текст и исполняемые модули) $100
ViewComp Spreadsheet (внутренний «термкэп»; вывод на принтер; руководство) $80
GNU Debugger (gdb) версия 3.5 (полный исходный текст и исполняемые модули) $60
Gillespie Pascal-to-C (использование достоинств Паскаля; читаемый и поддерживаемый вывод) $25
Gosling Spreadsheet Pack (варианты эл.таблиц Гослинга 1982; PubliCalc и SC версия 6.10; 1 для MS-DOS) $25
Хотите видеть свои программы в этом списке?...Обращайтесь к нам!
The Austin Code Works Телефон: (512) 258-0785
11100 Leaf wood Lane much more... ask for catalog Факс:(512)258-1342
Austin, Texas 78750-3587 USA E-mail: info@acw.com
Бесплатная доставка при предоплате MasterCard/VIS A
мышь
Завершая знакомство с периферийными устройствами
компьютера, рассмотрим особенности программирования одного из самых
маленьких приборов, официально именуемого "манипулятор
"мышь". (Я полагаю, что приведенного количества кавычек
достаточно, чтобы не использовать их далее в тексте и заранее
прошу прощения за возможные двусмысленности, связанные с этим.)
В отличие от своих живых собратьев компьютерная мышь не
приносит человеку вреда, однако и пользы от нее, на мой взгляд, в
большинстве случаев (за исключением разве что графических
редакторов) не много. Впрочем, это дело вкуса, а поскольку
большинство популярных программ так или иначе стремятся ее
поддерживать, а оболочка Microsoft Windows идеологически
построена на использовании мыши малоискушенным пользователем, мы
тоже займемся дрессировкой этого зверька.
Мы не стали рассматривать мышь в главе, посвященной работе с
асинхронным коммуникационным портом, по следующей
причине. Как вы уже знаете, плоттер и диджитайзер управляются
посредством определенных команд, характерных для этих устройств.
Нам необходимо только отправлять через СОМ1 эти команды и
получать оттуда же ответы на них. Мышь нам потребуется
только однажды присоединить к RS-232, а всю связь с ней возьмет на
себя специальный драйвер, поставляемый с ней в комплекте.
Некоторые породы мышей вообще не претендуют на СОМ1,
например Logitech Bus Mouse, присоединяемая к собственной плате
расширения, вставляемой в компьютер. Мыши же по имени
Logitech Mouscman Cordless вовсе не требуется проводов -
радиоволны связывают се с компьютером.
ря иа изобилие производителей мышей, существует
некоторая совместимость между различными мышами и их драйвера-
в^аГИыИГ?пЯ"МЫЩЬ G^eniUS Mouse Управляется достаточно
универсальным драйвером GMOUSE.COM. На нем мы и остановимся.
62
Драйвер GM0USE.COM поддерживает множество функций
мыши, в большинстве своем связанных с управлением курсором -
его наличием на экране и внешним видом, его положением и
пределами его перемещения, а также функций для определения
состояния кнопок мыши.
Обращение к драйверу осуществляется с помощью прерывания
INT33H. Для программирования той или иной функции мыши
нам остается только узнать ее номер и соответствующие
параметры, закладываемые в регистры перед прерыванием. По традиции
соберем всю необходимую информацию в header-файл и назовем
его <andy\mouse.h>. Ниже приведено содержимое этого файла.
/* <andy\mouse.h> - header file for mouse programming. */
/* V
«define ENABLED 0x01 /* включение чего-либо */
«define DISABLED 0x00 /* выключение чего-либо */
«define DFLT_CUR 0x01 /* обычный текстовый курсор */
«define USER_CUR 0x00 /* user-defined cursor */
/* Нажатие кнопки */
«define LEFT_B 0x01 /* левой */
«define RGHT_B ' 0x02 /* правой */
«define MIDL_B 0x04 /* средней */
/* Функции мыщи */
«define RESET 0x00
«define CUR_0N 0x01
«define CUR_0FF 0x02
«define READCUR 0x03
«define SETCUR 0x04
«define B_PRS 0x05
«define B_RLS 0x06
«define HORJANGE 0x07
«define VER_RANGE 0x08
63
«define
#define
«define
«define
«define
«define
«def i ne
«define
GRF_CUR
TXT_CUR
R£AD_MOVE
EVENT
PEN_ON
PEN_OFF
SENSE
NO RANGE
0x09
OxOA
OxOB
OxOC
OxOD
OxOE
OxOF
0x10
/* Event handler macros */
«define MOTION 0x01
«define LB_PRS 0x02
«define LB_RLS 0x04
«define RB_PRS 0x08
«define RB_RLS 0x10
«define MB_PRS 0x20
«define MB RLS 0x40
/* mouse is moved
/* left button pressed
/* left button released
/* right button pressed
/* right button released
/* middle button pressed
/* middle button released
/* Device
struct MOUSE
int status,
cursor,
buttons,
left,
right,
middle,-
x_pos,
y_pos,
xjnove,
yjnove;
status structure */
{
/* enabled/disabled
/* enabled/disabled
/* number of buttons
/* left button status
/* right button status
/* middle button status
/* horizontal coordinate
/* vertical coordinate
/* horizontal movement
/* vertical movement
extern struct MOUSE Mickey;
/* Function declarations */
void grfjnouse(int, int, int*);
void mouse_cursor(int);
void mouse_pen(int);
| void mouse_serfse(int, int);
64
void no_range(int far *);
void hrz_range(int, int);
void vrt_range(int> int)';
int read_prs_button(int);
int read_rls_button(int);
void readjnotion(void);
void readjnouse(void);
int resetjnouse(void);
void set_mouse^pos(int, int);
void txt_mouse(int, int, int);
/******* Конец файла <andy\mouse.h> *******/
Макросы мышиных функций никак не прокомментированы в
этом файле, поскольку их более подробное описание будет дано
непосредственно в заголовках текстов подпрограмм.
Полный список функций драйвера мыши, включающий еще 11
функций поддержки работы регистрового интерфейса EGA
(добавленных к прерыванию BIOS INT ЮН), приведен в приложении
к этому выпуску. Эти 11 функций (а точнее - 9, так как две из
них не используются) представляют собой не что иное, как
"заплатку" на неудачном стандарте адаптера EGA, большинство
регистров которого доступно только для записи, что затрудняет
определение положения курсора на экране, а также, часто и самого
текущего режима, что необходимо для согласования управления
мышью и се образом на экране.
Информацию о текущем состоянии мыши мы будем хранить в
структуре struct MOUSE. Переменная Mickey определена в
приведенном ниже файле, содержащем текст функции reset_mouse().
«include "mquse,h"
«include <dos.h>
/Л************************************************************/
/* V
/* DESCRIPTION: "resetjnouse" gives the current status of */
65
/* mouse hardware anfi driver. Use it before calling other */
/* mouse functions! */
/* ARGUMENTS: none. 7
/* RETURN: mouse status. V
/* GLOBAL VARS REFERENCED: Mickey. V
/* GLOBAL VARS MODIFIED: Mickey. V
/* V
struct MOUSE Mickey;
int resetjnouse(void)
{
union REGS inregs, outregs;
inregs.x.ax = RESET;
int86(0x33,&inregs,&outregs);
Mickey.status = (outregs.x.ax) ? ENABLED : DISABLED;
Mickey.buttons = outregs.x.bx;
return(Mickey.status);
В заголовке указано, что функцию необходимо, использовать в
самом начале работы с мышью. Она определяет, загружен ли
мышиный драйвер. Если нет, то использование прочих функций
мыши приведет к неопределенным результатам. Переменная
Mickey определена в одном файле с функцией reset_mouse(), с
тем чтобы никто не забывал включать эту функцию в программу.
Слово Mickey, правда, уже используется в качестве термина для
единицы физического перемещения мыши (введено Биллом
Гейтсом, президентом формы Microsoft). Если вас смущает это
совпадение (или авторитет Билла Гейтса), замените это название
переменной на какое-нибудь более удачное.
66
Функция resel_mouse() приводит мышь в состояние со
следующими параметрами:
Курсор Невидим
Текстовый курсор Инвертированное изображение
Графический курсор Стрелка
Маска событий Все нули
Эмуляция светового пера Включена
Отношение motion/pixel
по вертикали 16/8
по горизонтали 8/8
Область перемещения курсора (в пикселях)
по вертикали О/ширина экрана-1
по горизонтали 0/высота экрана-1
Смысл этих параметров станет более понятным из дальнейшего
изложения.
Рассмотрим остальные функции управления мышью. Начнем с
той, что занимается включением и выключением мышиного
курсора, то есть его появлением на экране и исчезновением.
ftinclude "mouse.h"
#include <dos.h>
/* V
/* DESCRIPTION: "mouse_cursor" switches cursor on/off. */
/* ARGUMENTS: cursor state (ENABLED/DISABLED). 7
/* RETURN: none. */
/* GLOBAL VARS REFERENCED: Mickey. */
/* GLOBAL VARS MODIFIED: Mickey. */
/* V
У*************************************************************/
void mouse_cursor(int state)
<
union REGS inregs, outregs;
inregs.x.ax = (state) ? CUR_0N : CUR_0FF;
int86(0x33.&inregs.ioutregs):
67
Mickey.cursor = state;
return;
Отметим, что мышиный курсор (в текстовом режиме), как
правило, является дополнительным по отношению к привычному нам
курсору, состоящему из мигающих горизонтальных линий.
Текстовый курсор мыши - это сплошной прямоугольник высотой и
шириной в один символ. Перемещаясь по экрану, он некоторым
образом видоизменяет атрибуты накрываемого им участка
дисплея (например, инвертируя их). Характер этих изменений
определяется двумя параметрами, передаваемыми функции
lxt_mouse(), позволяющей вам выбрать желаемый вид курсора.
Первый параметр - screen_mask - участвует в логической
операции AND с атрибутами данного участка экрана, после чего
выполняется XOR второго параметра - cursor_mask - и результатов
предыдущей операции. Оба эти параметра представляют собой
16-битовые величины, устроенные следующим образом (буква М
на рисунке обозначает мерцание):
М
Фон
Цвет
Символ на экране
15 14 12 11 8 7 0 Бит
Распределение битов в параметрах screen_mask и cursor_mask
совпадает с тем, что принято для обычного представления
атрибутов экрана. Инвертированное изображение получается при
передаче функции txt_mouse() значения параметра screen_mask,
равное OxFFFF, и cursor_mask - 0x7700. В качестве значения
переменной cursor для txt_mouse() в этом случае должен передаваться
макрос USER_CUR;
Можно избрать в качестве мышиного курсора обычный курсор
(hardware cursor). Значение переменной cursor для этого должно
представлять из себя макрос DFLT_CUR, а вместо screen_mask и
cursor_mask надо передать номера линий (scan lines) начала и
конца сканирования. Для монохромного дисплея эти номера варьи-
68
руют от 0 до 11, ллч цветного - от 0 до 7 (ноль обозначает самую
верхнюю линию канирования). Вот текст функции txtjnouseO:
«include "mouse.h"
«include <dos.h>
/* V
/* DESCRIPTION: "txtjrouse" chooses the hardware (<cursor>= */
/* = DFLT_CUR) or the software (<cursor> = USER_CUR) text V
/* cursor. <screen_mask> in the former case is the first */
/* and <cursor_mask> - last scan lines defining cursor. */
/* ARGUMENTS: screen and cursor masks (USER_CUR)/start and */
/* stop scan lines (DFLT_CUR). 7
/* RETURN: none. V
/* GLOBAL VARS REFERENCED: none. 7
/* GLOBAL VARS MODIFIED: none. V
/* V
/*************************************************************/
void txtjnouse(int cursor, int screenjnask, int cursorjnask)
{
union REGS inregs, outregs;
inregs.x.ax = TXT_CUR;
inregs.x.bx = cursor;
inregs. x. ex = screenjnask;
inregs.x.dx = cursor_mask;
int86(0x33,&inregs,&outregs);
return:
Достоинства мыши, связанные с наличием дополнительного
курсора, проявляются в графической моде. Драйвер GMOUSE.COM
выводит на экран маленькую стрелочку, указывающую в верхний
левый угол экрана, после выполнения функций reset_mouse() и
69
mouse_cursor(ENABLED). При желании можно легко
видоизменить облик этого графического курсора на любой другой. Для
этого достаточно просто воспользоваться функцией grf_mouse():
«include "mouse.h"
«include <dos.h>
у*************************************************************/
Л
Л
Л
Л
/*
г
/*
г
г
DESCRIPTION: "grfjnouse" defines the graphics mouse
cursor.
ARGUMENTS: hot spot (cursor active coordinates) and
one pointer to screen and cursor mask.
RETURN: none.
GLOBAL VARS REFERENCED: none.
GLOBAL VARS MODIFIED: none.
7
7
V
7
7
V
7
7
7
/*************************************************************/
void grf_mouse(int x_hot, int y_hot, int *mask)
<
union REGS inregs, outregs;
inregs.x.ax = GRF_CUR;
inregs.x.bx = x_hot;
inregs.x.ex = y_hot;
inregs.x.dx = mask;■
int86(0x33,&inregs, &outregs);
return;
Пользоваться этой функцией не намного сложнее, чем написать
ее. Внешний вид графического курсора, как и текстового,
определяется двумя масками - маской экрана и маской курсора, над
которыми в той же последовательности выполняются те же логичес-
70
кие операции - AND и XOR. Разница лишь в том, что на каждую
из графических масок отводится не по 16 бит, а по 16 16-битовых
величин. Маски передаются с помощью одного указателя на них,
поэтому проще всего представить их в виде массива
int mask[2][16];
указатель на который и передается функции grf_mouse(). С
помощью следующей элементарной таблицы, представляющей
результаты взаимодействия двух этих масок, вы легко сможете
создать графический курсор любого вида (в пределах возможностей
вашего адаптера).
Результат на экране
О
1
Не изменится
Инверсия
Два других параметра - x_hot и y_hot - указывают на так
называемую горячую точку графического курсора (hot spot).
Координаты графического курсора, используемые остальными
мышиными функциями, представляют собой координаты именно горячей
точки. Функции grf_mouse() передаются, естественно, не
абсолютные, а относительные значения этих координат (считая от
левого верхнего угла экранного прямоугольника, занятого курсором).
Раз уж мы перешли к координатам, рассмотрим связанные с
ними функции мыши. Во-первых, приведем текст функции
set_mouse_pos(), предназначенной для установки мышиного
курсора в указанную позицию:
Маска экрана
0
0
1
1
Маска курсора
0
1
0
1
«include "mouse.h"
«include <dos.h>
/* 7
/* DESCRIPTION: "set_mouse_pos" sets current mouse cursor */
71
/* location. */
/* ARGUMENTS: <x> horisontal and <y> vertical coordinates. */
/* RETURN: none. */
/* GLOBAL VARS REFERENCED: Mickey. */
/* GLOBAL VARS MODIFIED: Mickey. */
/* V
void set_mouse_pos(int x, int y)
<
union REGS inregs, outregs;
inregs.x.ax = SETCUR;
inregs.x.ex = Mickey.x_pos = x;
inregs.x.dx = Mickey.y_pos = y;
int86(0x33,&inregs,&outregs);
return;
Я полагаю, что эта функция не вызовет у вас каких-либо
вопросов. Помимо установки курсора в определенную точку экрана
можно задать для него область перемещения, за пределы которой
он не сможет выйти (при попытке выхода курсор остается в
пределах указанной области). Это позволяют сделать две
приведенные ниже функции - hrz_range() и vrt_range().
«include "mouse.h"
«include <dos.h>
/* V
/* DESCRIPTION: "hrz_range" defines norizontal range and */
/* "vrt_range" - vertical range of .cursor location. */
/* ARGUMENTS: minimal and maximal cursor coordinates. */
/* RETURN: none. */
72
/* GLOBAL VARS REFERENCED: none. ' 7
/* GLOBAL VARS MODIFIED: none. 7
/* 7
void hrz_range(int from, int to)
{
union REGS inregs, outregs;
inregs.x.ax = H0R_RANGE;
inregs. x. ex = from;
inregs.x.dx = to;
int86(0x33,&inregs,&outregs);
return;
}
void vrt_range(int from, int to)
{
union REGS inregs, outregs;
inregs.x.ax = VER_RANGE;
inregs.x.ex = from;
inregs.x.dx = to;
int86(0x33,Ainregs,Aoutregs);
return;
Кроме того, существует полезная функция, делающая курсор
невидимым после его вхождения в определенную область экрана.
Она устроена чуть сложнее прочих функций этой главы, так как
в ней используется far-указатель на область, играющую в данном
случае роль шапки-невидимки.
73
«include "mouse.h"
«include <dos.h>
У*************************************************************/
Л V
/* DESCRIPTION: "no_range" specifies screen range where 7
/* cursor is disabled. */
/* ARGUMENTS: pointer to range rectangular coordinates. 7
/* RETURN: none. 7
/* GLOBAL VARS REFERENCED: none. 7
/* GLOBAL VARS MODIFIED: none. 7
/* 7
/*************************************************************/
void no range(int far * range)
{
union REGS inregs, outregs;
struct SREGS segregs;
inregs. x.ax = NOJANGE;
segread(&segregs); /* set the segment registers */
segregs.es.= FP_SEG(range); /* segment 7
inregs.x.dx = FP_0FF(range); /* offset */
int86x(0x33, &inregs, &outregs, isegregs);
return;
Массив range представляет собой просто четыре числа,
соответствующие координатам левого верхнего и правого нижнего углов
этой области. Стоит отметить, что курсор, однажды войдя в
заколдованную область, исчезает там до следующего вызова функции
mouse_cursor (ENABLED).
74
Помимо возможности что-либо, совершить с установкой координат
мыши, их можно еще определить. Функции, выполняющие это,
позволяют узнать состояние кнопок мыши к этому времени.
Функции read_prs_button() и rcad_rls_button() сообщают, сколько раз
кнопка мыши, определенная переменной button, была нажата или
отпущена с момента последнего обращения к функции (с тем же
параметром). В элементах x_pos и y_pos структуры,
представляемой переменной Mickey, записываются координаты курсора при
последнем нажатии (освобождении) кнопки. Кнопки мыши
обозначаются очевидными макросами LEFT_B, RGHT_B и MIDL_B.
«include "mouse.h"
«include <dos.h>
у*************************************************************/
Г 7
/* DESCRIPTION: "read_prs_button" reads press state and 7
/* "read_rls_button" - release state of specified <button>. */
/* ARGUMENTS: button mask. */
/* RETURN: number of presses/releases since the last call */
I* GLOBAL VARS REFERENCED: Mickey. */
/* GLOBAL VARS MODIFIED: Mickey. 7
/* 7
у*************************************************************/
int read_prs_button(int button)
{
union REGS inregs, outregs;
inregs.x.ax = B_PRS;
inregs.x.bx = button/2;
int86(0x33,&inregs,&outregs);
Mickey.x_pos = outregs.x.ex;
Mickey.y_pos = outregs.x.dx;
return(outregs.x.bx);
}
int read_rls_button(int button)
75
union REGS inrogs, oulrcgs;
inregs.x.ax = B_RLS;
inregs.x.bx = button/2;
int86(0x33,&inregs,&outregs);
Mickey. x_pos = outregs.x.cx;
Mickey. y_pos = outregs.x.dx;
return(outregs.x.bx);
Состояние кнопок мыши в конкретный момент времени можно
узнать, воспользовавшись еще одной функцией запроса -
read_mouse(). Она сообщает информацию о всех трех (или двух)
кнопках вместе с текущими координатами мышиного курсора.
В одном файле с ней приведена функция очистки кнопок
clear_butlons(), совсем нехитрая, но содержащая процедуру,
необходимую для последующего обращения к read_mouse().
«include "mouse.h"
«include <dos.h>
/* * * * i
r v
/* DESCRIPTION: "read_mouse" reads mouse state: button */
/* state and cursor location. V
/* ARGUMENTS: none. 7
/* RETURN: none. 7
/* GLOBAL VARS REFERENCED: Mickey. 7
/* GLOBAL VARS MODIFIED: Mickey. V
/* 7
void readjnouse(voicl)
{
76
union REGS inregs, outregs;
inregs.x.ax = READCUR;
int86(0x33. &i nregs, Soutregs);
Mickey, left = outregs. x.bx & LEFT_B;
Mickey.right = outregs.x.bx & RGHT_E
Mickey.middle = outregs.x.bx & MIDL_
Mickey.x_pos = outregs.x.cx:
Mickey.y_pos = outregs.x.dx;'
return:
r v
/* DESCRIPTION: "clear_buttons" clears mouse buttons to 0. */
/* ARGUMENTS: none. V
/* RETURN: none. */
/* GLOBAL VARS REFERENCED: Mickey. */
Г GLOBAL VARS MODIFIED: Mickey. V
/* V
void clearj.buttons(void)
{
Mickey.left = Mickey.right = Mickey.middle = 0;
}
Для того чтобы ваша программа реагировала на нажатие каких-
либо кнопок мыши (например, левой), проще всего
воспользоваться следующей формой обращения к функции read_mouse():
while(!Mickey.left) read_mouse():
clear_buttons();
77
Существует более сложная форма, основанная на использовании
функции, связывающей некоторое событие, случившееся с
мышью (например, нажатие кнопки или перемещение мыши), с
определенной процедурой. Адрес этой процедуры передается
функции (ей соответствует макрос EVENT в <andy\mousc.h>) вместе с
маской события. Возможные маски перечислены под заголовком
/* Event handler macros */ в том же header-файле. Для
написания этой функции вместо С лучше использовать ассемблер, так
что более подробно мы не станем се рассматривать.
Точно так же мы оставим в стороне несколько дополнительных
функций, таких, как включение/выключение светового пера,
определение отношения числа движений мыши по горизонтали и
вертикали к движению курсора на экране, определение порога
скорости и функции, связанные с использованием дисплейного
адаптера EGA.
У нас, правда, остались неиспользованными два элемента в
переменной Mickey: x_move и ymovc, соответствующие перемещению
мышиного курсора. Чтобы не разбрасываться переменными
попусту, приведем напоследок функцию rcad_molion(),
определяющую это перемещение за время, прошедшее с момента последнего
вызова read_motion():
«include "mouse.h"
«include <dos.h>
у******************-*******************************************/
/* 7
/* DESCRIPTION: "readjnotion" reads mouse motion since */
/* the last call. */
/* ARGUMENTS: none. V
/* RETURN: none. */
/* GLOBAL VARS REFERENCED: Mickey. */
/* GLOBAL VARS MODIFIED: Mickey. */
Л 7
void readjnotion(void)
78
{
union REGS inregs, outregs;
inregs.x.ax = READ_M0VE;
int86(0x33,&inregs,&outregs);-
Mickey.x_move = outregs.x.ex;
Mickey.yjnove = outregs.x.dx;
return;
Положительные значения x_move и y_move указывают на
перемещение курсора мыши соответственно вправо и вниз. Думаю, не
вызывает сомнений, что обозначают отрицательные величины.
Перемещение измеряется в движениях мыши (mouse motion),
каждое из которых составляет примерно 1/200 дюйма (чуть более
одного миллиметра).
По умолчанию, как уже было приведено выше, 16 движений
мыши по вертикали и 8 - по горизонтали эквивалентны
перемещению курсора на 8 пикселей на экране в том же направлении.
Таким образом, зная число движений мыши, можно определить
реальное перемещение курсора за определенное время. Если,
однако, область перемещения курсора ограничена с помощью
функций hrz_range() и vrt_range(), могут""возникнуть некоторые
недоразумения. Функция read_motion() будет продолжать
отсчитывать физическое перемещение мыши и при выходе за пределы
области, тогда как экранные координаты курсора в
действительности не будут изменяться.
Приведем текст демонстрационной программы, использующей
некоторые из приведенных выше функций управления мышью.
«include "mouse.h"
«'include <clos.h>
«include <graph.h>
«include <conio.h>
79
niain()
{
int x,y;
int mask[2][16];
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[0
mask[
mask[
mask[
mask[
mask[
mask[
mask[
mask[
mask[
mask[
mask[
mask[
mask[
mask[
mask[
Screen mask
[0]=0xFFFF
[1]=0xFFFF
[2]=0xFFFF
[3]=0xFFFF
[4]=0xFFFF
[5]=0xF00F
[6]=0x0000
[7]=0x0000
[8]=0x0000
[9]=0x0000
[10]=0xF00F
[11]=0xFFFF
[12]=0xFFFF
[13]=0xFFFF
[14]=0xFFFF
[15]=0xFFFF
Cursor mask
[0]=0x0000
[1]=0x0000
[2]=0x0000
[3]=0x0000
[4]=0x0000
[5]=0x0000
[6]=0x07E0
[7]=0x7FFE
[8]=0x7FFE
[9]=0x07E0
[Ю]=0х0000
[11]=0x0000
[12]=0x0000
[13]=0x0000
[14]=0x0000
80
mask[1][15]=0x0000;
_clearscreen(_GCLEARSCREEN);
if(!reset_mouse()) {
printf("Install mouse driver, please.");
exit(-1);
}
printf(" A. Grigorjev. С for PC. Mouse functions demo.");
printf("\n Press SPACE bar after each function ");
printf ("(or click specified button if neccesar-y).");
printf("\n\n No cursor after reset_mouse(). ");
printf("Your mouse has %d buttons.",Mickey.butuos);
getch();
mouse_cursor(ENABLED);
printf("\n\n Cursor appears after ");
printf(*'mouse_cursor(ENABLED). Try to move it.");
getch();
mouse_cursor(DISABLED);
mouse_cursor(DISABLED).");
printf("\n And disappears again after
mouse_cursor(DISABLED).");
getch();
txt_fflOuse(DFLT_CUR,0x00,0x01);
roouse_cursor(ENABLED);
printf("\n\n Hardware cursor after mouse_cursor(ENABLED) ");
printf("and txt_mouse(DFLT_CUR....).");
getch();
txt^mouseCUSER^UR. OxFFFF, 0x7700);
mouse_Cursor(ENABLED);
printf("\n User-defined cursor after
txt_mouse(USER_CUR....).");<
getch();
hrz_range( 0.200);
vrt_range(0.100);
81
mouse_cursor(ENABLED);
read_prs_button( LEFT_B);..
read_rts_button(LEFT_B);
read_prs_button(RGHT_B);
read_rts_button(RGHT_B);
read_motion();
printf("\n\n Cursor after hrz_range(0,200) and
vrt_range(0,100).");
printf("\n Move mouse now and click buttons several times.");
getch();
printf("\n\n Left button was pressed
%d",read_prs_b'utton(LEFT_B));
printf(" times, released %d times.".reader Is_button(LEFT_B));
printf("\n Rignt button was pressed
%d",read_prs_button(RGHT_B)); i
printf(" times, released %d times.", read_rls_button(RGHT_B));
read_motion();
printf("\n Horisontat movement - (%d)
motions.", Mickey.y_move);
motions,".Mickey.x_move);
printf(" vertical movement - (%d) motions.",Mickey.y_move);
set_mouse_pos(0,0);
printf("\n\n Mouse cursor is now set in (0,0) position.");
getch();
set_mouse_pos(100,100);
printf("\n And now in (100,100) position. ");
printf("That's set_mouse_pos() action.");
printf("\n\n Now about mouse cursor in graphics mode.");
printf("\n We'll use read_mouse() to continue. ");
.printf("Press left button.");
while(!Mickey.left) read_mouse();
ctear_buttons();
_setvideomode(_HRESBW);
_rectangle(_GFILLINTERIOR,100,100,500.250);
mouse_cursor(ENABLED);
printf("\n Default cursor in graphics. ");
82
printf("Cursor range was not changed.");
printf("\n We'll use again read_mouse() to continue. ");
printf("Press right button.");
while(!Mickey.right) read_mouse();
clear_buttons();
printf("\n\n User-defined cursor in graphics.");
grf_mouse(0,0,mask);
if(Mickey.buttons == 2){
printf("\n Press both buttons to7return to ");
printf("default cursor shape.");
while(!Mickey.left || {Mickey.right) read_mouse();
>
else{
printf("\n Press middle button to return to ");
printf("default cursor shape.");
while(!Mickey.middle) read_mouse();
}
clear_buttons();
reset_mouse();
mouse_cursor(ENABLED);
printf("\n\n That was done by reset_mouse() and ");
printf("mouse_cursor(ENABLED) again.");
printf("\n\n 0k! That's all. Now press a key or ");
printf("mouse button to exit.");
printf("\n kbhit() and read_mouse() allow a program ");
printf ("to detect them.")';
while(!kbhit() && IMickey.left && IMickey. right \
&& IMickey.middle)
read_mouse();
_setvideomode(_DEFAULTMODE);
printf("\n Use mouse functions and have fun!");
}
Надеюсь, что после знакомства с этой программой у вас вообще
не останется вопросов, 'за исключением, может быть, такого:
"Почему здесь так много printf'oB?". Co своей стороны, у меня
тоже нет вопросов к вам на этот раз.
83
ДОПОЛНИТЕЛЬНЫЕ ВОЗМОЖНОСТИ
КОМПОНОВЩИКА
Ниже описываются возможности управления компоновкой:
изменение моды чтения-записи файлов, управление размещением
стека и динамической области памяти, и некоторые вопросы,
связанные с использованием оверлейных структур в программах.
Текстовая и бинарная моды чтения-записи
MS-DOS обычно интерпретирует файлы, содержащие данные
(data files) как "текстовые". Текстовая мода чтения-записи
характеризуется тем, что комбинации управляющих символов
возврата каретки и перевода на следующую строку ("carriage return" и
"line feed") при вводе преобразуются в одиночный символ
перевода на следующую строку ("line feed"). При выводе происходит
обратное преобразование - перед каждым символом перевода на
следующую строку ставится символ возврата каретки. Бинарная
мода подавляет трансформацию управляющих символов.
Включив файл BINMODE.OBJ в программный список программы,
вы смените текстовую моду, принимаемую по умолчанию,
например библиотечной функцией fopen(), на бинарную. Несмотря на
это изменение, вы можете явно задать текстовую моду при
открытии того или иного файла с помощью той же функции fopcnO.
Управление размещением стека и динамической
области памяти
Программы, скомпилированные и скомпонованные под
управлением Microsoft С, работают с фиксированным размером стека
(стандартный размер 2048 байт). Стек располагается над
данными типа static, а динамическая область использует память,
оставшуюся над стеком (как описано в нулевом выпуске). Однако для
84
некоторых программ модель с фиксированным размером стека Hf
является идеальном; для них было бы более приемлемым, чтобы
сгек и динамическая область делили между собой намять.
Подвязывание объектных файлов типа mVARSTCK.OBJ дает вам
такую возможность: если динамическая область выходит за
пределы памяти, она пытается использовать для себя свободную
стековую память до тех пор, пока на дойдет до вершины стека. Как
только занятая в стеке память освобождается, она снова
становится доступной для стека.
Размер стека не может вырасти сверх последнего размещенного
элемента динамической области в стеке или сверх размера,
заданного в процессе компоновки, если в стеке нет элементов
динамической области. Кроме того, динамическая область может
занимать память, отведенную под стек, а стек не может занимать
память, выделенною под динамическую область.
С помощью компоновки программы с одним из объектных файлов
типа mVARSTCK.OBJ (m - первая буква используемой
библиотеки) можно изменить модель, используемую для размещения
динамической области. Данные файлы являются версиями процедур
для малой, средней, компактной, большой моделей памяти,
которые позволяют функциям распределения памяти (malloc, calloc,
_expand, _fmalloc, _nmalloc, realloc) размещать элементы данных
динамической области в неиспользованном стековом
пространстве, если они выходят за пределы памяти. Например,
CL /AM PRG.C MVARSTCK
Данная командная строка компилирует файл PRG.C в средней
модели памяти, а затем связывает результирующий объектный
модуль с файлом MVARTSCK.OBJ - объектным файлом
переменного размера стека для средней модели памяти.
Если вы связываете программу с файлами mVARSTCK.OBJ, не
подавляйте контроль за переполнением стека с помощью
директивы pragma check_stack или опций компилятора /Gs или /Ох.
В программах с данной опцией переполнение стека может
произойти, вызвав ошибки, которые затем будет трудно обнаружить.
85
Использование оверлейных структур
Если программа не умещается к имеющуюся намять, то с
помощью программы LINK можно создать онерлейную версию
программы. В оверлейной версии, отдельные части программы,
известные под названием '"оверлей" (от английского overlay -
"перекрываться"), загружаются при необходимости. Части программы
разделяют между собой одну и ту же область памяти.
Оверлейным может быть только программный код, данные
оверлейными не бывают. Программы, использующие оверлейные
структуры, обычно требуют меньше памяти, но они работают
медленнее, т. к. требуется дополнительное время для считывания
кода с диска в память. Оверлейные части определяются
посредством заключения их в скобки при задании списка объектных
файлов для компоновщика. Каждый модуль или комбинация модулей.
в скобках обозначают один оверлей. Зададим в командной строке
LINK в поле objfiles следующий список объектных файлов:
а + (о+с) +■ (e+f) + g + (i)
В данном примере модули (b+c), (e+f) и (i) являются оверлеями.
Как только управление передастся на данные модули, они считы-
ваются с диска в память. Модули а и g, а также любые модули из
библиотек составляют "резидентную часть" (или "корень")
вашей программы. Оверлеи загружаются в один и тот же раздел
памяти, так, что в отдельный момент времени резидентным
является только один модуль. Дублирующиеся имена различных
оверлеев не поддерживаются.
Компоновщик передает вызовы из корня в оверлей и вызовы из
одного оверлея в другой с помощью прерывания, за которым
следует идентификатор модуля и смещение. По умолчанию номер
прерывания 63 (3F в шестнадцатеричной системе). Для
изменения номера прерывания вы можете воспользоваться опцией
компоновщика /О. Эта опция должна применяться только в
программах, использующих оверлейные структуры и порождающих
другие программы, использующие оверлейные структуры. В данном
случае каждая программа должна использовать отдельный номер
86
оверлейного прерывания, откуда следует, что мня ом или., -ф..!
рамма должна быть скомпонована с данной опцией.
Ограничения па оверлеи
Оверлейными можно сделать тс модули, на которые передается
управление и возвращается с помощью стандартного 32-битоього
вызова' или возврата. Однако вызовы функций, определяемые
ключевым словом near, являются 16-битовыми. Это означает, что
нельзя сделать оверлейными модули, содержащие "ближние"
функции, если остальные модули вызывают данные процедуры.
Запросы диспетчера оверлейных структур
Диспетчер оверлейных структур - часть стандартных библиотек,
поставляемых с компилятором Microsoft С (QuickC). Если во
время процесса компоновки объявить оверлеи, код диспетчера
автоматически будет связан с остальными модулями программы.
j
Во время работы выполняемого файла оверлейный диспетчер
ищет файл, который требует загрузить другой оверлей. Он ищет
данный файл сначала в текущем рабочем каталоге, затем, если
файл ис найден, диспетчер просматривает каталоги, заданные в
переменной операционной среды PATH. Как только диспетчер
найдет файл, он извлечет оверлейные модули, заданные корневой
программой. Если оверлейный диспетчер не может найти
требуемый оверлейный файл, он запрашивает имя файла.
При использовании оверлейных структур компоновщик
производит только один файл .ЕХЕ. Файл открывается до тех пор, пока
диспетчер не потребует извлечь новые оверлейные модули.
Например, предположим, что выполняемая программа,
вызывающая файл OVERLAY.EXE, который не существует ни в текущем
каталоге, ни в каталогах, заданных переменной PATH,
использует оверлеи. Если пользователь запускает ее с помощью полной
спецификации маршрута, оверлейный диспетчер высвечивает
следующее сообщение при попытке загрузить оверлейные файлы:
Cannot find OVERLAY.EXE
Please enter new program spec:
(Невозможно найти файл OVERLAY.EXE
87
Пожалуйста, введите новую программную спецификацию:)
Пользователь может ввести спецификацию дисковода или каталог
(либо то и другое), определяющие местоположение программы
OVERLAY.EXE. Например, если файл расположен на дисководе В
в каталоге \PRINTER\PSTSCRPT, пользователь вводит либо
B:\PRINTER\PSTSCRPT '
либо просто
\PRINTER\PSTSCRPT
если дисковод В является текущим.
Если пользователь позже удалит диск из устройства В и
оверлейному диспетчеру потребуется снова доступ к оверлею, он не
найдет файл OVERLAY.EXE. Высветится следующее сообщение:
Please insert diskette containing
B:\PRINTER\PSTSCRPT\OVERLAY.EXE in drive 8:
and strike any key when ready
(Пожалуйста, вставьте дискету, содержащую файл
B:\PRINTER\PSTSCRPT\OVERlAY.EXE, в дисковод В:
и при готовности нажмите любую клавишу)
После того как оверлейный файл будет прочитан с диска,
оверлейный диспетчер высветит следующее сообщение:
Please restore the original diskette.
Strike any key when ready
(Пожалуйста, установите первоначальную дискету.
При готовности нажмите любую клавишу)
88
ПРИЛОЖЕНИЕ А
Сообщения об ошибках компоновщика
i
Данный раздел описывает сообщения об ошибках, генерируемые
компоновщиком LINK. Сообщения имеют следующий формат:
место возникновения: error/warning Lxxxx: текст сообщения
В данных сообщениях поле "место возникновения" - это название
входного файла, в котором обнаружена ошибка,, либо сама
программа LINK, если входного файла нет. Если входной файл имеет
расширение .OBJ или .LIB и известно имя модуля, имя модуля
заключается в скобки, как показано в следующем примере:
SLIBC.LIB(file)
MAlN.OBJ(main.c)
TEXT.OBJ
Ошибки компоновщика могут возникнуть при неявном его вызове
с помощью команд CL/QCL, при явном вызове с помощью
команды LINK и при компиляции программы, имеющей программный
список, или когда вы создаете на диске выполняемый файл в
среде QuickC. Если ошибка компоновщика возникает в программной
среде QuickC, QuickC высвечивает сообщение: "Ошибка в
процессе компоновки. Выполняемого файла не создается":
Errors during link phase
No .EXE file produced
View I ink errors?
Yes
No
Cancel
89
Для просмотра ошибок компоновщика нажмите ENTER. Ошибки
последнего прохода компоновщика хранятся в файле с именем
LINK.ERR. В файле может быть следующее:
symbol defined
INIT.OBJ(C:\OLD\init.c) : error L2025: _scrn
more than once pos: 249 Record type: 90
LINK : error L2029: Unresolved externals:
jnain in file(s): $$QC$$.OBJ(QUICKLIB)
There were 2 errors detected
Ниже приведены ошибки, возникающие во время компоновки
объектных файлов с помощью Microsoft Overlay Linker, LINK.
Фатальные ошибки компоновщика
При возникновении фатальной ошибки компоновщик прерывает
выполнение. Сообщения имеют следующий формат:
место возникновения: fatal'error L1xxx: текст сообщения.
Номер Сообщение о фатальной ошибке компоновщика
L1001 '"option': имя опции неясно".
После индикатора опции (/) не появилось
уникального имени опции. Например, команда
| Link /N main;
сгенерирует ошибку, поскольку программа LINK не
может определить, какая из опций, начинающихся на
букву N, имеется в виду
L1002 '"option': нераспознанное имя опции".
За индикатором опции (/) появился нераспознанный
символ, как в следующем примере:
I LINK /ABCDEF main;
90
LI003 "/(JllICKUH, I EX ЕР ЛС К несовместимые опции".
Вы не можете компоновать с опциями /QUICKLIB и
/ЕХЕРАСК одновременно
LI004 '"option': неверное числовое значение".
Для одной из опций было задано некорректное
числовое значение. Например, для опции, требующей
числовое значение, задана символьная строка
LI006 '"option': размер стека превышает 65535 байт".
Размер, определенный для стека, больше 65535 байт
LI007 '"option': номер прерывания превышает 255".
В качестве значения опции /OVERLAYINTERRUPT
задано число, более 255
L1008 '"option': большое предельное число сегментов".
Было установлено предельное число сегментов,
большее 3072 (с помощью опции /SEGMENTS)
L1009 "option': CPARMAXALLOC: некорректное значение".
Число, определенное в опции /CPARMAXALLOC не
лежит в пределах 1 - 65535
L1020 "Не заданы объектные модули".
Для компоновщика нет имен объектных файлов
LI021 "Файлы соответствий вкладывать невозможно".
Один файл соответствий оказался внутри другого
файла соответствий
L1022 "Строка файла соответствий слишком длинна".
Строка в файле соответствий длиннее 127 символов
LI023 "Выполнение прекращено пользователем".
Вы нажали CONTROL+C
LI024 "Вложение правых скобок".
В командной строке содержимое оверлея было
написано некорректно
L1025 "Вложение левых скобок".
В командной строке содержимое оверлея было
написано некорректно
L1026 "Несоответствие правых скобок".
В командной строке в спецификации содержимого
оверлея пропущена правая скобка
L1027 "Несоответствие левых скобок".
В командной строке в спецификации содержимого
оверлея пропущена левая скобка
L1043 "Таблица распределения памяти переполнена".
В программе задано более 32768 длинных вызовов
(long calls), длинных переходов (long jumps) либо
других длинных указателей (long pointers). Попытайтесь
заменить длинные ссылки короткими, если возможно,
и перестроить объектный модуль
L1045 "Слишком много записей TYPDEF".
Л Объектный модуль содержит более 255 записей
TYPDEF. Данные записи описывают общие
переменные. Такая ошибка может возникнуть только в
программах, созданных компилятором Microsoft FORTRAN
или другими компиляторами, поддерживающими
общие переменные. (TYPDEF - это термин
операционной системы DOS. Он разъясняется в документе
"Справочное руководство программиста по
операционной системе MS-DOS фирмы Microsoft" или в других
справочных книгах по DOS)
L1046 "Слишком много внешних имен в одном модуле".
В объектном модуле определено более 1023 внешних
имен. Разбейте модуль на меньшие части
92
1.1047 "Слишком много имен групп, сегментов,
классов в одном модуле".
Программа содержит слишком много имен групп,
сегментов, классов. Сократите число групп, сегментов
или классов и перестройте объектный файл
L1048 "Слишком много сегментов в одном модуле".
Объектный модуль имеет более 255 сегментов.
Расщепите модуль или объедините сегменты
L1049 "Слишком много сегментов".
Программа имеет более чем максимально разрешенное
число сегментов (опция /SEGMENTS определяет
максимально разрешенное число; стандартно 128).
Повторите компоновку с опцией /SEGMENTS с
соответствующим числом сегментов
L-1050 "Слишком много групп в одном модуле".
Программа LINK обнаружила более 21 определения
групп (GRPDEF) в одном модуле. Сократите число
определений групп или расщепите модуль
L1051 "Слишком много групп".
В программе определено более 20 групп, не считая
DGROUP. Сократите количество групп
L1052 "Слишком много библиотек".
Была сделана попытка скомпоновать более 32
библиотек. Объедините библиотеки либо используйте модули,
требующие меньшего количества библиотек
L1053 "Переполнение таблицы имен".
Компоновщик не имеет достаточно места (более 256
килобайт) для размещения таблицы имен программы
(таких, как глобальные, внешние, имена сегментов,
групп, классов, файлов). Объедините модули или
сегменты и перестройте объектные файлы. Исключите
столько глобальных имен, сколько возможно
93
1.1054 'Требуемое количество сегментов слишком велико".
Компоновщик не имеет достаточно памяти для
размещения таблицы, описывающей количество требуемых
сегментов (стандартное число 128 или значение,
определенное в опции /SEGMENTS). Повторите
компоновку снова, используя опцию /SEGMENTS для задания
меньшего количества сегментов (например, 64, если
ранее было использовано стандартное значение), либо
освободите некоторое количество памяти путем
удаления резидентных программ или параллельных задач
LI056 "Слишком много оверлеев".
В программе определено более 63 оверлеев
L1057 "Запись данных слишком велика".
Запись LEDATA (в объектном модуле) содержит более
1024 байт данных. Это ошибка транслятора (LEDATA
- это термин операционной системы DOS, его
объяснение можно найти в документе "Справочное
руководство программиста по MS-DOS фирмы Microsoft" или в
других справочных книгах по DOS). Обратите
внимание, какой транслятор (компилятор или ассемблер)
построил некорректный объектный модуль и при
каких обстоятельствах. Пожалуйста, сообщите о данной
ошибке, используя форму Product Assistance Request
LI070 '"name': размер сегмента превышает 64К".
Заданный сегмент содержит более 64К кода или
данных. Повторите компиляцию и компоновку в большой
модели памяти
LI071 "Сегмент _ТЕХТ больше 65520 байт".
Вероятнее всего, ошибка может случиться только в
Сопрограммах малой модели памяти, и она может
произойти, если любая программа с сегментом, названным
_ТЕХТ, компонуется посредством команды LINK с
опцией /DOSSEG. Программы на языке С малой модели
памяти должны резервировать адреса кода 0 и 1; для
целей выравнивания этот предел увеличивается до 16
94
LI072 "Общая область больше 65536 байт".
Программа имеет более 64К общих переменных.
Данная ошибка не может возникнуть в объектных
файлах, сгенерированных с помощью Макроассемблера
MASM (Microsoft Macro Assembler). Она возникает
только в программах, полученных с помощью
компилятора Microsoft FORTRAN или других компиляторов,
поддерживающих общие переменные
L1080 "Невозможно открыть файл-листинг".
Диск или корневой каталог переполнены. Удалите или
переместите файлы, чтобы освободить место
L1081 "Переполнение при записи выполняемого файла".
Диск, на который записывается выполняемый файл
.ЕХЕ, переполнен. Освободите место на диске и
повторите компоновку
L1083 "Невозможно открыть выполняемый файл".
Диск или корневой каталог переполнены. Удалите или
переместите файлы, чтобы освободить место
L1084 "Невозможно создать временный файл".
Диск или корневой каталог переполнены. Освободите
место на диске и повторите компоновку
L1085 "Невозможно открыть временный файл".
Диск или корневой каталог переполнены. Удалите или
переместите файлы, чтобы освободить место
LI086 "Не хватает временного файла".
Заметьте обстоятельства возникновения данной
ситуации и свяжитесь с фирмой Microsoft Corporation,
воспользовавшись формой "Product Assitance Request"
LI087 "Неожиданный конец временного файла".
Диск с временным выходным файлом компоновщика
удален
95
1.1 OSS "Переполнение при записи файла-листинга".
При записи на диск файла-листинга диск
переполнился. Освободите место на диске, повторите компоновку
L10S9 "filename': невозможно открыть
файл соответствий".
Программа LINK не может найти заданный файл
соответствий. Обычно причина этого - опечатка при
задании имени файла
L1090 "Повторно открыть файл-листинг невозможно".
По одному из указанных здесь запросов программы
LINK диск не был заменен. Повторите компоновку
L1091 "Неожиданный конец файла в библиотеке".
Диск, содержащий библиотеку, был, вероятно, удален.
Установите диск, содержащий библиотеку и повторите
компоновку
L1093 '"filename': объектный файл не найден".
Компоновщик не может найти заданного объектного
файла. Задайте правильное имя объектного файла и
повторите компоновку
L1101 "Некорректный объектный модуль".
Один из объектных модулей является некорректным.
Если данная ошибка произошла после
перекомпиляции, свяжитесь с фирмой Microsoft, воспользовавшись
формой "Product Assilance Request"
L1102 "Неожиданный конец файла".
Для библиотеки был обнаружен некорректный форма'!
L1103 "Попытка обращения к данным, лежащим
за границами сегмента".
Заданная запись в объектном модуле продолжена м\
границы сегмента. Это ошибка транслятора. Замен.к
какой транслятор 'компилятор или ассемблер» ин.ы i
некорректный объектный мол\ль. и o6cuwir.ii.nn,i. i
96
которых он был создан. Пожалуйста, сообщите о
данной ситуации в фирму Microsoft Corporation, восполь-
( зовавшись формой "Product-Assistance Request"
LI 104 "'filename': некорректная библиотека".
Заданный файл не является корректным
библиотечным файлом. Данная ошибка прекращает работу
программы LINK
LI 113 "Неразрешенная COMDEF; системная ошибка".
Заметьте обстоятельства возникновения данного сбоя и
свяжитесь с фирмой Microsoft Corporation,
воспользовавшись формой "Product Assistance Request"
LI 114 "Файл не подходит для IEXEPACK: повторите
компоновку без опции IEXEPACK".
В компонуемой программе размер упакованного
загружаемого образа плюс упаковочный код больше, чем
неупакованный загружаемый образ. Повторите
компоновку без опции /EXEPACK
L1115 "/QUICKLIB несовместима с оверлеями".
Вы не можете задать оверлей и компоновать с опцией
/QUICKLIB одновременно
L1126 "Стартовый адрес aulstart не найден".
Попытка создать библиотеку типа Quick Library без
компоновки с соответствующей стандартной
библиотекой (с расширением .LIB)
Нефатальные ошибки компоновщика
Нсфатальные ошибки выявляют проблемы в выполняемом файле.
Компоновщик LINK создает выполняемый файл. Нефатальные
ошибки имеют следующий формат:
место возникновения: error L2xxx: текст сообщения
97
Номер Сообщение об ошибке компоновщика
L2001 "Запись fixup(s) безданных".
Запись FIXUPP не имеет предшествующей записи
данных (возможно, ошибка компилятора)
L2002 "Переполнение записи fixup при "ближнем" вызове
'number' в сегменте рамки (frame segment) 'segname',
сегменте назначения (target segment) 'segname'
с отступом (target offset) 'number"'.
Данную ошибку могут вызвать следующие условия:
- Программа компилируется в малой модели памяти с
опцией /NT.
- Размер группы больше 64К.
- Программа содержит междусегментные короткие
переходы (inlrsegment short jumps) или
междусегментные короткие вызовы (intrsegment short calls).
- Имя элемента данных не соответствует процедуре из
библиотеки, подключенной при компоновке.
- Объявление EXTRN в исходном файле на языке
ассемблер появилось в теле сегмента, например:
code SEGMENT public 'CODE'
EXTRN main:far
start PR ОС far
call main
ret
Ostart ENDP
code ENDS
Предпочтительна следующая конструкция:
EXTRN main:far
code SEGMENT public 'CODE'
start PR ОС far
call main
start ENDP
code ENDS
Перепишите исходный файл и перестройте объектный
файл. (Подробную информация о frame segment и
target segment вы найдете в "Справочном руководстве
программиста по MS-DOS фирмы Microsoft")
98
L2003 "Дальний вызов на данные собственного сегмента".
Дальние вызовы на данные собственного сегмента не
разрешаются
L2004 "Переполнение записи fixup типа LOBYTE".
fixup типа LOBYTE вызвала переполнение адреса
L2005 "Тип fixup не поддерживается".
Тип fixup не поддерживается компоновщиком фирмы
Microsoft (вероятно, ошибка компилятора). Обратите
внимание на обстоятельства данной ошибки и
сообщите их в Microsoft Corporation, воспользовавшись
формой "Product Assistance Request"
L2011 '"пате': конфликт NEAR/HUGE".
Конфликтные аттрибуты NEAR и HUGE в объявлении
общих переменных. Такая ошибка может возникнуть
только в программах, созданных компилятором
Microsoft FORTRAN или другими компиляторами,
поддерживающими общие переменные
L2012 '"пате': несоответствие размера элемента массива".
"Дальний" общий массив был описан различными
размерами элементов массива. Например, первый раз
массив был описан как массив символов, а второй
раз - как массив действительных чисел
L2013 "Запись LIDATA слишком велика".
Запись LIDATA в объектном модуле имеет размер
более 512 байт, максимально разрешенного размера. Это
ошибка компилятора. Сообщите об условиях
возникновения данной ошибки в фирму Microsoft,
воспользовавшись формой "Product Assistance Request"
L2024 '"пате.- имя уже определено".
Одно из специальных оверлейных имен, требуемое для
поддержки оверлеев, определено в объектном файле
99
L2025 "пате': имя определено более одного раза".
Удалите из объектного файла лишние определения
имен
L2029 "Неразрешимые внешние ссылки".
В одном или более модулях одно или более имен
описаны как внешние, но они не были определены ни в
одном из модулей или библиотек. После сообщения
появляется список неразрешимых внешних ссылок,
как показано в следующем примере:
_PEXIT in filc(s):
MAIN.OBJ (main.c)
_POPEN in file(s):
MAIN.OBJ (main.c)
Имя, которое идет перед 'in file(s)', - это
неразрешимое внешнее имя. В следующей строке - список
объектных модулей, имеющих ссылки на данное имя. Это
сообщение и список записываются также в файл карты
распределения памяти, если он существует
L2041 "Стек плюс данные превышает 64К".
Общий размер стекового сегмента программы плюс
DGROUP превышает 64К; в результате программа
загружается неверно
L2043 "Стартовый адрес aulstart не найден".
Если вы строите библиотеку типа Quick Library с
использованием опции /Q, компоновщик требует имя
aulstart, определенное, как стартовый адрес
L2044 '"пате': имя уже определено,
используйте опцию /NOE".
Компоновщик интерпретирует данную ситуацию как
переопределение глобального имени, уже имеющегося
в стандартной библиотеке. Перекомпонуйте программу
с опцией /NOEXTDICTIONARY
100
Предупреждения компоновщика
Предупреждения также обозначают возможные проблемы в
выполняемом файле. Компоновщик LINK строит выполняемый файл.
Предупреждения имеют следующий формат:
моею возникновения: warning L4xxx: текст сообщения I
Номер Предупреждающее сообщение компоновщика
L4003 "Неразрешенный вызов: смещение offset".
Данная ошибка может быть вызвана компиляцией
программы в малой модели памяти с опцией /NT
L4012 "Опция /HIGH отменяет /ЕХЕРАСК".
Опции /HIGH и /ЕХЕРАСК не могут быть
использованы одновременно
L4015 "Опция /CODEVIEW отменяет / DS ALLOC ATE".
Опции /CODEVIEW и /DSALLOCATE не могут быть
использованы одновременно. L4016 "Опция
/CODEVIEW отменяет /ЕХЕРАСК", они не могут
быть использованы одновременно
L4020 '"пате': размер сегмента кода превышает 65500".
Сегмент кода размером 65501 - 65536 байт может быть
обработан некорректно на процессоре Intel 80286
L4021 "Нет стекового сегмента".
Программа не имеет стекового сегмента,
определенного с типом STACK. Данное сообщение не возникнет
при обработке модулей, скомпилированных с помощью
компилятора Microsoft С, но может возникнуть для
модулей Макроассемблера. Обычно каждая программа
должна иметь стековый сегмент с типом объединения
STACK. Есл и есть причины не определять стек или
определить его без типа объединения STACK, можно
проигнорировать сообщение. Оно может быть получено
101
и при компоновке с помощью LINK версий 2.40 и
ниже, поскольку эти компоновшики просматривают
библиотеки один раз
L4031 '"пате': сегмент описан более чем в одной группе".
Сегмент был описан как член двух различных групп.
Исправьте исходный файл и перестройте объектные
L4034 "Более 239 оверлейных сегментов;
лишние помещены в корень".
В оверлеях не может быть объявлено более 239
кодовых сегментов. Все сегменты свыше данного предела
помещаются в корень
L4045 "Имя выходного файла 'пате"'.
Компоновщик высветил в запросе "Run file" стандарт-
нос выходное имя файла, но, поскольку была
использована опция /Q, имя выходного файла было изменено
L4050 "Слишком много глобальных имен".
Для получения отсортированного списка глобальных
имен в файле распределения памяти была
использована опция /MAP, но задано слишком много имен
(более 2048 имен по умолчанию). Повторите компоновку
с опцией /MAP:numbcr. Компоновщик выдает
неотсортированный список имен
L4051 '"filename': невозможно найти библиотеку".
Компоновщик не может найти заданный файл.
Введите новое имя, новую спецификацию маршрута или и
то и другое
L4053 "VM.TMP: некорректное имя файла; игнорируется".
Имя VM.TMP появилось как имя объектного файла.
Переименуйте файл и повторите компоновку
L4054 '"filename': невозможно найти файл".
Компоновщик не может найти заданный файл.
Введите новое имя файла либо новую спецификацию пути,
либо и то и другое
102
ПРИЛОЖЕНИЕ В
Сообщения об ошибках утилиты МАКЕ
Ошибки, высвечиваемые в процессе работы утилиты поддержки
программ Microsoft (MAKE), имеют один из следующих
форматов:
{filenamel MAKE}: fatal error/warning Uxxxx:-текст сообщении
Сообщения начинаются с имени входного файла (filename), если
он существует, либо с имени утилиты. Если возможно, утилита
МАКЕ печатает предупреждение и продолжает работу. В
некоторых случаях ошибки являются неисправимыми и утилита МАКЕ
прекращает работу. Сообщения, генерируемые утилитой МАКЕ,
перечислены ниже.
Фатальные ошибки утилиты МАКЕ
При возникновении фатальной ошибки утилита МАКЕ прерывает
выполнение. Сообщения о фатальных ошибках имеют следующий
формат:
{filenamel MAKE}: fatal error Шххх: текст сообщения
Номер Сообщения об ошибках утилиты МАКЕ
U1001 "Макроопределение больше, чем number".
Было определено макро, имеющее значение строки,
длиннее установленного числа, разрешающего
максимальную длину. Попытайтесь переписать файл
описаний утилиты МАКЕ и расщепить макро
103
U1002 "Бесконечно рекурсивное макро".
Был определен циклический вызов макрокоманд, как
в следующем примере:
А=$(В)
В=$(С) }
С=$(А)
U1003 "Выход за пределы памяти".
Во время обработки файла описаний утилита МАКЕ
вышла за пределы памяти. Попытайтесь сократить
размер файла описаний утилиты МАКЕ путем его
реорганизации или расщепления на.меньшие
U1004 "Синтаксическая ошибка:
пропущено имя макрокоманды".
Файл описаний утилиты МАКЕ содержит
макроопределение без левой части (то есть строки,
начинающейся с '=')
U1005 "Синтаксическая ошибка: пропущено двоеточие".
В строке, которая должна содержать
выходной/входной файл, не хватает двоеточия, разделяющего
выходной файл и входной файл. Утилита МАКЕ ожидает
любую строку, за которой следует пустая строка,
чтобы считать ее строкой выходного/входного файла
U1006 '"targetname': макрорасширение больше числа 'number".
Макрорасширение плюс длина любой строки, с
которой оно может быть объединено, длиннее
установленного числа. Попытайтесь перезаписать файл описаний
утилиты МАКЕ, расщепив макро на два меньших
U1007 "Много источников".
Правило вывода было определено более одного раза
U1008 '"пате': невозможно найти файл или каталог".
Заданный файл или каталог не может быть найден
U1009 '"command': список аргументов слишком длинный".
Командная строка в файле описаний утилиты МАКЕ
длиннее 128 байтов (разрешенный максимум для
DOS). Перепишите команды, чтобы сделать список
аргументов короче
Ш010 '"filename': отказ доступа".
Файл, определенный как 'filename', имеет атрибут
"только-чтение"
ШОП '"filename': не хватает памяти".
Для выполнения программы утилите МАКЕ не хватает
памяти
U1012 '"filename': неизвестная ошибка".
Заметьте обстоятельства возникновения данной
ошибки и сообщите о них фирме Microsoft Corporation,
воспользовавшись бланком "Product Assistance Request"
Ш013 mcommand': ошибка encode".
Одна из программ или команд, вызванная в файле
описаний утилиты МАКЕ, завершилась с ненулевым
кодом завершения
U1015 '"file': целевой файл не существует".
Обычно это не означает ошибку. Данное сообщение
предупреждает пользователя о том, что целевой файл
не существует. Утилита МАКЕ выполняет любые
команды, заданные в блоке описаний, поскольку в
большинстве случаев зыходной файл создастся последней
командой файла описаний утилиты МАКЕ
105
Предупреждения утилиты МАКЕ
Предупреждения также обозначают возможные проблемы п рябо
те утилиты МЛКЕ.
Номер Предупреждающее сообщение утилиты МЛКЕ
U4000 '"filename": не существует".
Обычно это сообщение не свидетельствует об ошибке.
Оно предупреждает пользователя о том, что
указанный файл не существует. МАКЕ выполняет все
команды, заданные в блоке, так как в большинстве случаев
отсутствующий файл будет создан последующими
командами файла МАКЕ
U4001 "Зависимый файл 'filename' не существует; целевой
файл filename' не строится".
Утилита МАКЕ не может продолжать, т. к. требуемый
входной файл не существует. Удостоверьтесь, что все
имена файлов присутствуют и что все они корректно
описываются в файле описаний утилиты МАКЕ
U4Q13 '"command': ошибка encode (игнорируется)".
Одна из программ или команд, вызванных в файле
описаний утилиты МАКЕ, возвратила ненулевой код
ошибки, в то время как утилита МАКЕ работала с
опцией /I. Ошибки игнорируются, и утилита продолжает
работу
U4014 "Синтаксис: MAKE options [name=value...] file options
'tin] [Id] f/ij [Is] [Ix file]"
Утилита MAKE была неправильно вызвана. Стартуйте
утилиту заново, воспользовавшись синтаксисом,
представленным в сообщении:
МАКЕ опции [имя=значение...] имя файла опции =
. f/n] l/d] [/i] [/si f/xfile]
106
ПРИЛОЖЕНИЕ С
Список функций драйвера мыши
Основные функции
Номер Назначение функции
0 Начальная установка драйвера и чтение текущего
состояния
1 Сделать курсор видимым
2 Сделать курсор невидимым
3 Определение местоположения курсора и состояния
кнопок
4 Установка курсора в заданную позицию
5 Определение количества нажатий на кнопку
6 Определение количества отпусканий кнопки
7 Установка диапазона перемещения курсора по
горизонтали
8 Установка диапазона перемещения курсора по
вертикали
9 Установка параметров графического курсора
10 Установка параметров текстового курсора
11 Определение перемещения курсора
12 Установка адреса процедуры обработки события
13 Включение эмуляции светового пера
14 Выключение эмуляции светового пера
15 Установка отношения motion/pixel
16 Запрет появления курсора в некоторой области экрана
17 Установка параметров большого графического курсора
IX' Не используется
107
19 Установка порога удвоенной скорости
20 Установка временной процедуры обработки
прерывания
21 Определение размера буфера для сохранения
состояния драйвера
22 Сохранение состояния драйвера
23 Восстановление состояния драйвера
24 Установка альтернативной процедуры обработки
прерывания
25 Определение вектора прерывания, указывающего на
установленную процедуру обработки прерывания
26 Установка порога чувствительности
27 Определение порога чувствительности.
28 Установка частоты прерываний
29 Установка текущей страницы видеопамяти
30 Определение номера текущей страницы видеопамяти
31 Запрет работы драйвера
32 Восстановление работы драйвера
33 Начальная установка драйвера
34 Установка языка диалога (английский и т. д.)
35 Определение языка диалога .,
36 Определение дополнительной аппаратной информации
(тип интерфейса, номер запроса прерывания IRQ)
Функции библиотеки регистрового интерфейса EGA
(EGA Register Interface Library)
240 Чтение данных из отдельного регистра EGA '
241 Запись данных в отдельный регистр EGA
242 Чтение данных из последовательной группы регистров
указанного порта EGA
243 Запись данных в последовательную группу регистров
указанного порта EGA
108
21]
245
246
247
248
249
250
Запись данных из отдельной группы регистров
указанного порта EGA -^
Запись данных в отдельную группу регистров
указанного порта EGA
Установка начальных значений регистров
Задание начальных значений регистров
Не используется
Не используется
Определение состояния библиотеки регистрового
интерфейса
■ЮОЩ ON!
'"■si ,;,
109