Subversion Repositories Kolibri OS

Rev

Rev 7496 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
7544 leency 1
2
3
	Документация на C--
4
	
5
	
26
27
28

Содержание

29
30
31
1      Введение.
7496 leency 32
1.1    История создания и развития.
7544 leency 33
1.2    Что такое C--?
34
1.3    Как установить C--.
7496 leency 35
7544 leency 36
2.     Управление компиляцией.
7496 leency 37
2.1    Параметры командной строки компилятора C--.
7544 leency 38
2.1.1  /ON - Оптимизация числовых выражений.
39
2.1.2  /DE - Временное расширение разрядности переменной.
40
2.1.3  /ARGC - Альтернативный обработчик командной строки.
41
2.1.4  /OST - слияние одинаковых строковых констант.
42
2.1.5  /D - установка идентификатора в TRUE из командной строки.
43
2.1.6  /IA - упрощенный ввод ассемблерных инструкций.
44
2.1.7  /CRI - пропуск повторно включаемого файла.
45
2.1.8  /IND - импорт имен процедур из DLL.
46
2.1.9  /WS - задать имя stub файла для программ под windows.
47
2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции.
48
2.1.11 /DBG - создание отладочной информации.
49
2.1.12 /J0 /J1 /J2.
50
2.1.13 /LST - Создание ассемблерного листинга.
51
2.1.14 /ENV - Сохранение адреса переменных окружения.
52
2.1.15 /CPA - Очистка post-области данных.
53
2.1.16 /W - вывод предупреждений.
54
2.1.17 /NW - Выборочное отключение типов предупреждений.
55
2.1.18 /WSI - короткая таблица импорта.
56
2.2    Директивы транслятора.
57
2.2.1  ?ifdef/?ifndef
58
2.2.2  ?initallvar
59
2.2.3  ?usestartup
60
2.2.4  ?startusevar
61
2.2.5  ?atexit
62
2.2.6  ?startuptomain
63
2.2.7  ?undef
64
2.2.8  ?align и ?aligncode
65
2.2.9  ?pragma
7496 leency 66
7544 leency 67
3.     Константы.
7496 leency 68
3.1    Числовые константы.
7544 leency 69
3.2    Символьные константы.
70
3.3    Строковые константы.
71
3.4    Постоянные выражения.
7496 leency 72
7544 leency 73
4.     Выражения.
7496 leency 74
4.1    Типы выражений.
7544 leency 75
4.2    Выражения типа EAX/AX/AL.
76
4.3    Выражения использующие получатель при вычислении выражения.
77
4.4    Не - EAX/AX/AL выражения.
78
4.5    Условные выражения.
79
4.5.1  Простые условные выражения.
80
4.5.2  Сложные условные выражения.
81
4.6    Изменение типа выражения при присваивании.
82
4.7    Вычисление в регистры EAX/AX/AL со знаком.
7496 leency 83
7544 leency 84
5.     Идентификаторы.
7496 leency 85
5.1    Формат идентификатора.
7544 leency 86
5.2    Зарезервированные идентификаторы.
87
5.3    Универсальные регистры для 16 и 32-битного режима.
88
5.4    Предопределенные идентификаторы.
7496 leency 89
7544 leency 90
6.     Переменные.
7496 leency 91
6.1    Типы переменных.
7544 leency 92
6.2    Объявление переменных.
93
6.3    Глобальные переменные.
94
6.4    Локальные переменные.
95
6.5    Динамические переменные и структуры.
96
6.6    Присваивание одного значения нескольким переменным.
97
6.7    Переменные типа float.
7496 leency 98
6.7.1  Формат переменных типа float.
7544 leency 99
6.7.2  Константы с плавающей точкой.
100
6.7.3  Диапазон допустимых значений.
101
6.7.4  Математические операции.
102
6.7.5  Преобразования типов.
103
6.7.6  Операции сравнения.
104
6.7.7  Сравнение переменных типа float с 32-битным регистром.
105
6.8    Указатели.
7496 leency 106
7544 leency 107
7.     Адресация.
7496 leency 108
7.1    Относительная адресация.
7544 leency 109
7.2    Абсолютная адресация.
7496 leency 110
7544 leency 111
8.     Работа с блоками данных.
7496 leency 112
8.1    Структуры.
113
8.1.1  Что такое структуры.
7544 leency 114
8.1.2  Синтаксис.
115
8.1.3  Инициализация структур при объявлении.
116
8.1.4  Инициализация структуры при выполнении программы.
117
8.1.5  Операции с элементами структур.
118
8.1.6  Вложенные структуры.
119
8.1.7  Отображение тега структуры на блок памяти.
120
8.1.8  Битовые поля структур.
121
8.2    Объединения.
122
8.3    Команды FROM и EXTRACT.
7496 leency 123
7544 leency 124
9.     Операторы.
7496 leency 125
9.1    Условные инструкции.
7544 leency 126
9.2    Циклы do{} while.
127
9.3    Циклы loop, LOOPNZ, loopnz.
128
9.4    Цикл while, WHILE.
129
9.5    Цикл for, FOR.
130
9.6    Оператор переключатель switch.
131
9.7    Оператор перехода goto, GOTO.
132
9.8    Оператор разрыва break, BREAK.
133
9.9    Оператор продолжения continue, CONTINUE.
134
9.10   Логическое объединение условий.
135
9.11   Переход через циклы.
136
9.12   Инвертирование флага проверки условий.
137
9.13   Вычисление выражения, а затем проверка условия.
138
9.14   Проверка битов при операции сравнения.
139
9.15   Оператор перестановки.
140
9.16   Оператор отрицания.
141
9.17   Оператор инверсии.
142
9.18   Специальные условные выражения.
143
9.19   Символ $ - вставляет текущий адрес программы.
144
9.20   Ключевое слово static и оператор ::.
145
9.21   Оператор sizeof.
146
9.22   Метки перехода.
7496 leency 147
7544 leency 148
10.    Ассемблер.
7496 leency 149
10.1   Поддержка команд ассемблера.
7544 leency 150
10.2   Ключевое слово asm.
151
10.3   Префикс dup - повторение инструкций DB/DW/DD.
152
10.4   Инструкции процессора Pentium III.
7496 leency 153
7544 leency 154
11.    Процедуры.
7496 leency 155
11.1   Типы процедур, функций и макрокоманд.
7544 leency 156
11.2   Стековые процедуры.
157
11.3   Регистровые процедуры.
158
11.4   Динамические процедуры.
159
11.4.1 Установка динамической процедуры в определенное место программы.
160
11.5   inline-процедуры.
161
11.5.1 Другое применение inline.
162
11.6   Процедуры обработки прерываний.
163
11.7   Замена return на goto.
164
11.8   Возвращаемые значения.
165
11.9   Объявление параметров в регистровых процедурах.
166
11.10  Объявление параметров в стековых процедурах.
167
11.11  Использование макрокоманд.
168
11.12  Передача параметров в стековые процедуры через регистры.
169
11.13  Вызов процедур с адресом в регистре.
170
11.14  Встоенные в компилятор процедуры.
171
11.14.1 Процедуры ABORT, ATEXIT и EXIT.
172
11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и
7496 leency 173
        outportd.
7544 leency 174
11.14.3 Процедуры для работы с вещественными числами.
175
11.15  Классы.
7496 leency 176
11.15.1 Объявление процедур в структурах.
7544 leency 177
11.15.2 Наследование.
178
11.15.3 Наследование процедур.
7496 leency 179
7544 leency 180
12.    Типы выходных файлов.
7496 leency 181
12.1   Выходные файлы типа COM.
7544 leency 182
12.2   Выходные файлы типа EXE.
183
12.3   Выходной файл *.EXE с моделью памяти tiny.
184
12.4   Объектный выходной файл OBJ.
185
12.5   COM файл symbiosis.
7496 leency 186
12.5.1 СИМБИОЗ - что это такое?
7544 leency 187
12.5.2 Как это делать.
188
12.5.3 Использование.
189
12.5.4 Злоупотребления.
190
12.6   SYS - драйверы устройств.
191
12.7   Компиляция кода расширителей ROM-BIOS.
192
12.8   32-битные файлы.
7496 leency 193
12.8.1 32-битный код под DOS.
7544 leency 194
12.8.2 32-битный код под Windows.
195
12.8.3 Вызов API процедур по ординалам.
196
12.8.4 Создание DLL под Windows.
197
12.8.5 Инициализация DLL при загрузке.
198
12.8.6 Компиляция ресурсов.
199
12.9   Выходные файлы для MeOS.
7496 leency 200
7544 leency 201
13.    Приложения.
7496 leency 202
13.1   Поиск включаемых файлов.
7544 leency 203
13.2   Регистры, которые должны быть сохранены.
204
13.3   C--.ini файл.
205
13.4   startup.h-- файл.
206
13.5   mainlib.ldp файл.
207
13.6   C-- символы.
208
7496 leency 209
 
210
 
7544 leency 211

1. Вступление.

7496 leency 212
 
213
  1.1 История создания и развития.
7544 leency 214
7496 leency 215
 
216
      Автором языка SPHINX C-- является Peter Cellik (CANADA). Последняя
217
  авторская версия SPHINX C-- v0.203 от 28.Oct.96. К сожалению автор
218
  отказался от дальнейшего развития языка. С 1998 года, уже почти умерший
219
  проект, подхватил Михаил Шекер (Россия). Изначально компилятор был freeware
220
  (и даже greenware, как его называл Peter Cellik). Таким статус компилятора
221
  остался и поныне.
222
 
223
      Первоначально компилятор мог создавать только *.com файлы и был
224
  рассчитан на создание небольших demo-программ и резидентов (TSR). В
225
  дальнейшем возможности компилятора расширялись, так как этого требовало
226
  наше бурное время.
227
 
228
      При развитии компилятора, было стремление придерживаться следующих
229
  принципов:
230
 
231
      1. Максимально возможная совместимость синтаксиса с последней версией
232
  компилятора написанного Peter Cellik. Это давало возможность с минимальными
233
  затратами (а чаще всего без всяких затрат) адаптировать программы,
234
  написанные для 0.203 версии компилятора, к последней на этот момент версии
235
  компилятора.
236
 
237
      2. Сблизить синтаксис компилятора со стандартным языком C. Это могло
238
  значительно облегчить перенос программ написанных на C.
239
 
240
      3. Также прилагались усилия, для того, чтобы человек знающий только
241
  ассемблер мог бы с минимальными затратами освоить C--.
242
 
243
      Вот эти, зачастую противоречащие друг другу принципы, влияли на выбор
244
  реализации возможностей компилятора. Насколько это удалось - судить Вам.
245
 
246
      Если у Вас есть предложения и идеи по улучшению компилятора - пишите.
247
  Мой e-mail sheker@mail.ru . Я с удовольствием выслушаю Ваши предложения, но
248
  не гарантирую, что все они будут реализованы. Если реализовывать все
249
  поступающие предложения, то компилятор превратится в свалку. Но если Ваше
250
  предложение будет ценным (на мой взгляд, так что Вам придется свое
251
  предложение хорошо аргументировать) и его будет возможным реализовать, оно
252
  без сомнения найдет место в компиляторе.
7544 leency 253
Return to contents.
7496 leency 254
 
255
 
7544 leency 256

7496 leency 257
  1.2 Что такое C--?
7544 leency 258
7496 leency 259
 
260
      C-- был разработан, для того чтобы строить маленькие и быстрые
261
  программы. Это наиболее подходит для создания резидентных программ (TSR),
262
  программ, требующих обработку прерываний или программ у которых ограничены
263
  ресурсы.
264
 
265
      C-- занимает промежуточное положение между си и ассемблером. В связи с
266
  этим промежуточным положением, Вам, для того чтобы писать программы на C--,
267
  необходимо знать и ассемблер и си. Если Вам надоело возиться с огромными
268
  ассемблерными листингами, а излишняя строгость языка C Вас угнетает, то этот
269
  язык для ВАС.
270
 
271
      Сейчас компилятор C-- может создавать 32-битные программы под Windows
272
  (EXE-файлы формата PE) и 32-битные программы под DOS (LE-формат). Имеет
273
  встроенный компилятор ресурсов и дизассемблер для генерации листинга
274
  откомпилированного файла. Поддерживает ассемблерные инструкции процессора
275
  Pentium III и ассемблерные инструкции FPU. Компилятор может генерировать
276
  отладочную информацию совместимую с отладчиками фирмы Borland. Компилятор
277
  может создавать объектные файлы (obj), но только для DOS программ.
278
 
279
      C-- разработан только для использования на компьютерах с процессорами
280
  совместимыми с семейством 80x86. Компилятор может работать только с
281
  операционными системами DOS и семейством Windows.
7544 leency 282
Return to contents.
7496 leency 283
 
284
 
7544 leency 285

7496 leency 286
  1.3 Как установить C--.
7544 leency 287
7496 leency 288
 
289
      Компилятору C-- для работы нужны совсем незначительные ресурсы:
290
  процессор 386 или лучше, чуть более 1 Мб дискового пространства и 4Мб
291
  оперативной памяти. Компилятор может быть установлен на компьютеры с
292
  операционной системой Windows 95 или лучше. Компилятор также может работать
293
  в среде чистого DOS. В основном пакете компилятора находится 32-битная DOS
294
  версия компилятора. На сайте http://sheker.chat.ru или
295
  http://c--sphinx.narod.ru можно найти и консольную версию компилятора.
296
  Консольная версия компилятора может работать только в среде Windows, но
297
  она, в отличие от DOS версии, может работать с длинными именами исходных
298
  файлов.
299
 
300
      Установить компилятор C-- на Ваш компьютер очень просто. Предположим,
301
  что Вы решили установить C-- на диск C. Создайте на диске C директорию
302
  (папку) с именем C-- или с другим, удобным и понятным для Вас именем
303
  (например, ДОСовской командой: MD C-- или другим доступным Вам способом).
304
  Затем с сайта http://sheker.chat.ru или http://c--sphinx.narod.ru скачайте
305
  файлы full_c--.zip и ful_c--2.zip и разархивируйте их в этой директории.
306
  Затем в файле autoexec.bat можно прописать путь к директории с
307
  компилятором. И все. Компилятор готов к работе. Если Вы добавляли путь к
308
  компилятору в файл autoexec.bat, то Вам придется перегрузить операционную
309
  систему.
310
 
311
      Переменная окружения для компилятора C-- задается либо из командной
312
  строки либо из командного файла (лучше всего ее прописать в autoexec.bat).
313
  Эта переменная должна указывать компилятору, где находятся его библиотечные
314
  файлы. Пример:
315
 
316
    set C--=c:\c--\lib
317
 
318
  Большой необходимости в переменной окружения для сегодняшней версии
319
  компилятора нет. Существует несколько других способов, указать компилятору
320
  место расположения библиотек. Поэтому определять или не определять
321
  переменную окружения дело вашего вкуса и привычек.
7544 leency 322
Return to contents.
7496 leency 323
 
324
 
7544 leency 325

7496 leency 326
2. Управление компиляцией.
327
 
328
  2.1 Параметры командной строки компилятора C--.
7544 leency 329
7496 leency 330
 
331
      Формат командной строки вызова компилятора C--:
332
 
333
  C-- [Параметры] [ИМЯ INI ФАЙЛА] [ИМЯ ИСХОДНОГО ФАЙЛА]
334
 
335
      Имя исходного файла можно задавать без расширения. Компилятор ищет
336
  файл с расширением c--, cmm, c.
337
 
338
      Параметры выделяются предшествующим символом / или -.
339
  Инвертировать функцию опции можно завершающим символом -.
340
 
341
  Список поддерживаемых параметров:
342
 
343
  /0          использовать только команды 8086/8088 процессора (установлено
344
              по умолчанию при компиляции 16-битных программ).
345
  /1          использовать команды 80186 процессора.
346
  /2          использовать команды и оптимизацию для 80286 процессора.
347
  /3          использовать команды и оптимизацию для 80386 процессора.
348
              (установлено по умолчанию для 32-битных программ).
349
  /4          использовать команды и оптимизацию для 80486 процессора.
350
  /5          использовать команды и оптимизацию для Pentium процессора.
351
  /6          использовать команды и оптимизацию для Pentium MMX процессора.
352
  /7          использовать команды и оптимизацию для Pentium Pro процессора.
353
  /8          использовать команды и оптимизацию для Pentium II процессора.
354
  /9          использовать команды и оптимизацию для Pentium III процессора
355
              (пока не реализовано из-за отсутствии информации).
356
  /A          выравнивание данных на четный адрес
357
              по умолчанию разрешено, поддерживает инверсию
358
  /AC         выравнивание адреса начала циклов
359
              по умолчанию отключено, поддерживает инверсию
360
              имеет смысл только на процессорах Pentium+
361
  /AL=##      установить значение байта заполнения при выравнивании данных
362
              по умолчанию 0.
363
  /AP         выравнивание адреса начала процедур.
364
              по умолчанию отключено, поддерживает инверсию
365
              имеет смысл только на процессорах Pentium и лучше
366
  /ARGC       вставить блок разбора командной строки
367
              по умолчанию отключено, поддерживает инверсию
368
  /AS         выравнивание в структурах.
369
              по умолчанию отключено, поддерживает инверсию
370
  /AT         вставить блок поддержки ATEXIT процедуры
371
              по умолчанию отключено, поддерживает инверсию
372
  /C          вставить блок игнорирования CTRL-C
373
              по умолчанию отключен, поддерживает инверсию
374
              имеет смысл только под DOS программы
375
  /CRI        проверять включаемые файлы на повторную загрузку
376
              по умолчанию включено, поддерживает инверсию
377
  /CPA        очистка post-области данных
378
  /D32        создать EXE файл (32 битный код под DOS)
379
              по умолчанию COM
380
  /D=idname   определить идентификатор для условной компиляции
381
              по умолчанию нет
382
  /DBG        генерировать отладочную информацию
383
              по умолчанию нет
384
  /DE         временное расширение разрядности после умножения
385
              по умолчанию отключено, поддерживает инверсию
386
  /DLL        создать DLL для Windows32
387
              по умолчанию COM
388
  /ENV        сохранение адреса переменных окружения
389
  /EXE        создать EXE файл для DOS (модель SMALL)
390
              по умолчанию COM
391
  /HELP /H /? справка, эта информация
392
  /IA         имена ассемблерных инструкций являются идентификаторами
393
              по умолчанию отключено, поддерживает инверсию
394
  /IND=name   импорт имен из файла name.
395
  /IP=path    задать путь поиска включаемых файлов
396
              по умолчанию нет
397
  /IV         инициализировать все переменные
398
              по умолчанию отключено, поддерживает инверсию
399
  /J0         не делать начальный jump на main()
400
              по умолчанию отключено, поддерживает инверсию
401
              В COM-файлах не создает jmp на main. В остальных не создается
402
              блок начальной инициализации программы, а управление
403
              передается сразу на main.
404
  /J1         делать короткий jump на main()
405
              по умолчанию нет
406
              имеет смысл только в COM-файлах
407
  /J2         делать jump на main()
408
              по умолчанию да, поддерживает инверсию
409
              имеет смысл только в COM-файлах
410
  /LAI        список поддерживаемых ассемблерных инструкций
411
  /LRS        загружать числовые константы через стек.
412
              по умолчанию да, поддерживает инверсию
413
  /LST        создать ассемблерный листинг
414
  /ME         показать мой адрес и имя
415
  /MEOS       создать исполняемый файл для MeOS
416
              по умолчанию COM
417
  /MER=##     установить максимальное число ошибок
418
              по умолчанию 16
419
  /MIF=file   определить имя главного компилируемого файла
420
  /NS         запретить подключать stub файлов
421
              по умолчанию нет, поддерживает инверсию
422
  /NW=##      выборочное отключение предупреждений
423
  /OBJ        создать OBJ файл
424
              только 16 битный код.
425
              по умолчанию COM
426
  /OC         оптимизировать по размеру кода
427
              по умолчанию нет, поддерживает инверсию
428
  /ON         оптимизация чисел
429
              по умолчанию нет, поддерживает инверсию
430
  /OS         оптимизация по скорости выполнения
431
              по умолчанию да, поддерживает инверсию
432
  /OST        оптимизация строковых идентификаторов
433
              по умолчанию отключено, поддерживает инверсию
434
  /P          вставить блок разборки командной строки
435
              по умолчанию нет, поддерживает инверсию
436
  /R          вставить блок уменьшающий размер доступной памяти.
437
              по умолчанию да, поддерживает инверсию
438
              имеет смысл только в DOS-файлах
439
  /S=#####    установить размер стека
440
              по умолчанию 2048
441
  /SA=####    начальное смещение адреса запуска программы
442
              имеет смысл только в COM-файлах, по умолчанию 0x100
443
  /SOBJ       создать ведомый OBJ файл
444
              по умолчанию COM
445
  /STM        перенести блок startup кода в процедуру main
446
              по умолчанию нет, поддерживает инверсию
447
              имеет смысл только в COM-файлах
448
  /SUV=####   начальный адрес не инициализированных переменных, при
449
              использовании ими startup кода.
450
              имеет смысл только в COM-файлах, по умолчанию равен /SA
451
  /SYM        надстройка для COM файла
452
              по умолчанию COM
453
  /SYS        создать драйвер устройств (SYS)
454
              по умолчанию COM
455
  /TEXE       создать EXE файл для DOS (модель TINY)
456
              по умолчанию COM
457
  /UL         использовать lea при оптимизации сложения регистров.
458
              по умолчанию да, поддерживает инверсию
459
  /UST        использовать startup код для переменных.
460
              имеет смысл только в COM-файлах
461
              по умолчанию нет, поддерживает инверсию
462
  /W          разрешить предупреждения
463
              по умолчанию нет, поддерживает инверсию
464
  /W32        создать EXE файл для Windows32 GUI
465
              по умолчанию COM
466
  /W32C       создать EXE файл для Windows32 console
467
              по умолчанию COM
468
  /WBSS       помещать не инициализированные данные в отдельную секцию.
469
              по умолчанию для /w32 разрешено, для остальных запрещено.
470
              поддерживает инверсию
471
  /WF=file    перенаправить вывод предупреждений в файл.
472
              по умолчанию нет
473
  /WFA        использовать быстрые вызовы API процедур
474
              по умолчанию да, поддерживает инверсию
475
              только под windows
476
  /WFU        создавать таблицу перемещений (для Windows32)
477
              по умолчанию нет, поддерживает инверсию
478
              только под windows
479
              для DLL устанавливается в да
480
  /WIB=#####  установить адрес image base
481
              по умолчанию 0x400000
482
  /WMB        создавать Windows-файл с единым блоком
483
              по умолчанию да, поддерживает инверсию
484
              только под windows
485
              для DLL устанавливается в нет
486
  /WORDS      выдать список зарезервированных идентификаторов
487
  /WS=name    указывает имя файла используемого в качестве stub под windows.
488
  /X          запретить вставлять в код SPHINXC-- сигнатуру
489
              по умолчанию разрешено, поддерживает инверсию
490
              отключается если есть J0
491
 
492
      Примечание: выражение поддерживает инверсию означает, что для данной
493
  опции можно использовать и противоположное значение с помощью символа -
494
  после опции. Пример:
495
 
496
  /WFA-
497
 
498
       Параметры командной строки можно писать как большими, так и
499
  маленькими буквами.
7544 leency 500
Return to contents.
7496 leency 501
 
502
 
7544 leency 503

7496 leency 504
    2.1.1 /ON - Оптимизация числовых выражений.
7544 leency 505
7496 leency 506
 
507
        При включении в командную строку опции /ON или в файл C--.INI строчки
508
    ON, компилятор будет анализировать операции над числами и где это
509
    можно, сокращать число операций. Например:
510
 
511
     Строка до оптимизации  | После оптимизации
512
    -----------------------------------------------
513
      AX = var + 7 - 3;     | AX = var + 4;
514
      AX = var * 2 * 5;     | AX = var * 10;
515
      AX = var * 2 / 4;     | AX = var / 2;
516
      AX = var * 10 / 2;    | AX = var * 5;
517
      AX = var / 2 / 3;     | AX = var / 6;
518
      AX = var / 4 * 8;     | AX = var * 2;
519
      AX = var / 16 * 16;   | AX = var;
520
 
521
        Возможные отрицательные последствия:
522
        Применение этой оптимизации может иметь и негативные последствия.
523
    Например, если Вам нужно выровнять значение переменной на границу
524
    параграфа, Вы напишите строку:
525
 
526
    var = var / 16 * 16;
527
 
528
    но после оптимизации будет
529
 
530
    var = var;
531
 
532
    т.е. выравнивание не будет  произведено. Этого можно избежать, если
533
    разбить это выражение на два:
534
 
535
    var = var / 16;
536
    var = var * 16;
537
 
538
    тогда оптимизация не будет произведена. Но для получения более
539
    компактного кода лучше будет записать так:
540
 
541
    AX = var;
542
    AX = AX / 16;
543
    AX = AX * 16;
544
    var = AX;
7544 leency 545
Return to contents.
7496 leency 546
 
547
 
7544 leency 548

7496 leency 549
    2.1.2 /DE - Временное расширение разрядности переменной.
7544 leency 550
7496 leency 551
 
552
        Как известно, после умножения может произойти переполнение, т.е
553
    разрядность результата может превысить разрядность исходных операндов и
554
    произойдет искажение результата. Частично решить эту проблему Вам поможет
555
    опция командной строки /DE или строка DE в файле C--.INI. После команды
556
    умножения компилятор будет просматривать остаток строки и если обнаружит,
557
    что расширение разрядности может быть востребовано (востребовать
558
    расширенную разрядность могут операции деления и вычисления остатка), то
559
    будут приняты меры по ее сохранению. Например:
560
 
561
      a = b*c+d/e; //здесь будет включена поддержка расширения разрядности
562
      a = b*c+d*e; //здесь поддержки расширения разрядности не будет.
563
 
564
        Однако применение этой опции может иметь и негативные последствия.
565
    Покажу это на примере:
566
 
567
    пусть имеется выражение
568
 
569
      a = b * c / d;
570
 
571
    если значения переменных b = 0xC000, c = 0x1000, d=0x10, после запуска
572
    такая программа зависнет с сообщением о том, что произошло переполнение
573
    при делении.
7544 leency 574
Return to contents.
7496 leency 575
 
576
 
7544 leency 577

7496 leency 578
    2.1.3 /ARGC - Альтернативный обработчик командной строки.
7544 leency 579
7496 leency 580
 
581
        Отличие этого обработчика командной строки от parsecommandline
582
    заключается в том, что при вызове PARAMSTR(0); Вы получите адрес строки в
583
    которой указан путь и имя запущенной программы. Следующие вызовы этой
584
    процедуры с увеличивающимся параметром будут возвращать адреса слов
585
    командной строки. А вызов процедуры PARAMCOUNT вернет Вам число слов в
586
    командной строке плюс один.
587
 
588
        Альтернативный обработчик командной строки включается директивой
589
    ?argc TRUE или из командной строки компилятора ключом /argc или
590
    строчкой argc в файле C--.INI.
7544 leency 591
Return to contents.
7496 leency 592
 
593
 
7544 leency 594

7496 leency 595
    2.1.4 /OST - слияние одинаковых строковых констант.
7544 leency 596
7496 leency 597
 
598
        Если этот режим оптимизации будет активизирован, то компилятор будет
599
    запоминать все строковые константы и при обнаружении одинаковых в код
600
    файла не будет вставлена повторная строковая константа, а будет сделана
601
    ссылка на первую, обнаруженную ранее строковую константу. В оптимизации
602
    участвуют только неименованные строковые константы. Т.е. если массив или
603
    структура будет инициализированы строкой, то такая строка не будет
604
    участвовать в процессе инициализации, так эта строка может быть изменена
605
    в процессе работы программы. Пример:
606
 
607
      char var="test";  //эта строка не будет участвовать в процессе
608
                        //оптимизации.
609
 
610
      void proc(){
7544 leency 611
        WRITESTR("test");       // эта строка будет участвовать в оптимизации.
7496 leency 612
        AX="test";          // переменной AX будет присвоен адрес строки,
613
                            // которая была вставлена в код программы в
614
                            // предыдущей строке.
615
      }
616
 
617
        Обо всех случаях обнаружения повторной строки компилятор будет
618
    выдавать предупреждения.
619
 
620
        Включается этот режим оптимизации либо с командной строки /ost, либо
621
    директивой #pragma option ost, либо строкой в файле c--.ini - ost.
622
    Отключить, включенный ранее, этот режим можно директивой #pragma option ost-.
7544 leency 623
Return to contents.
7496 leency 624
 
625
 
7544 leency 626

7496 leency 627
    2.1.5 /D - установка идентификатора в TRUE из командной строки.
7544 leency 628
7496 leency 629
 
630
        Если Вы написали программу, которая может компилироваться по разному,
631
    в зависимости от состояния некоторых идентификаторов (используется режим
632
    условной компиляции), то Вам очень может пригодится эта опция.
633
    Устанавливая с командной строки различные идентификаторы, Вы можете
634
    получать различные варианты программы, не редактируя исходный текст
635
    программы.
636
 
637
        Идентификатор вводится с командной строки ключом /d=idname.
7544 leency 638
Return to contents.
7496 leency 639
 
640
 
7544 leency 641

7496 leency 642
    2.1.6 /IA - упрощенный ввод ассемблерных инструкций.
7544 leency 643
7496 leency 644
 
645
        Стало возможным использовать ассемблерные инструкции без префикса $
646
    и вне блока asm. Этот режим включается: с командной строки опцией /ia;
647
    в файле конфигурации строкой ia или директивой #pragma option ia.
648
 
649
        Когда этот режим включен, все имена ассемблерных инструкций становятся
650
    зарезервированными словами, т.е. Вы не сможете эти имена использовать в
651
    качестве имен переменных или процедур. Ассемблерные инструкции компилятор
652
    распознает независимо от того, написаны они маленькими или большими
653
    буквами.
7544 leency 654
Return to contents.
7496 leency 655
 
656
 
7544 leency 657

7496 leency 658
    2.1.7 /CRI - пропуск повторно включаемого файла.
7544 leency 659
7496 leency 660
 
661
        Чаще всего, повторно включать файл в компилируемый проект, нет
662
    необходимости, но это иногда происходит из-за того, что некоторые
663
    включаемые файлы сами включают другие файлы. Чтобы этого не происходило
664
    приходится делать проверку на повторную загрузку файла. Теперь эту
665
    функцию берет на себя компилятор и у Вас отпадает необходимость делать
666
    эту проверку.
667
 
668
        Но иногда (очень редко) возникает потребность сделать повторное
669
    включение файла. Для этого в компиляторе есть опция командной строки
670
    /cri-, которая запрещает компилятору делать проверку на повторное
671
    включение. Соответственно, для c--.ini файла, это можно сделать строкой
672
    cri- или директивой в компилируемом файле - #pragma option cri-.
7544 leency 673
Return to contents.
7496 leency 674
 
675
 
7544 leency 676

7496 leency 677
    2.1.8 /IND - импорт имен процедур из DLL.
7544 leency 678
7496 leency 679
 
680
        Если Вы хотите в своей программе использовать DLL, для которой нет
681
    заголовочного файла с описанием процедур, то компилятор может
682
    импортировать имена из этой DLL. Для этого Вам надо указать имя этой
683
    библиотеки либо через опцию командной строки /ind=name.dll, либо в
684
    файле INI строкой 'ind=name.dll', либо через директиву '#pragma option
685
    ind=name.dll'.
686
 
687
        К недостатком такого способа получения имен можно отнести то, что при
688
    компиляции программы библиотека, из которой импортируются имена,
689
    обязательно должна присутствовать в компьютере. Также, если имена в
690
    библиотеке написаны без суффикса '@number', компилятор не будет
691
    контролировать число параметров передаваемых процедуре. И, к сожалению,
692
    компилятор умеет импортировать имена из библиотек имеющих только формат
693
    PE-файла.
7544 leency 694
Return to contents.
7496 leency 695
 
696
 
7544 leency 697

7496 leency 698
    2.1.9 /WS - задать имя stub файла для программ под windows.
7544 leency 699
7496 leency 700
 
701
        Как известно, в программах под windows есть DOS заглушка, называемая
702
    stub, которой передается управление при запуске такой программы в чистом
703
    DOS-е. Обычно такая заглушка выводит на экран сообщение о том, что эту
704
    программу надо запускать в среде windows.
705
 
706
        Вы можете вместо стандартного stub использовать свой. Для этого Вам
707
    необходимо указать имя 16-битного EXE-файла либо через опцию командной
708
    строки /ws=filename, либо строкой в INI-файле ws=filename, либо
709
    директивой #pragma option ws=filename.
710
 
711
        Таким образом, у Вас появилась возможность создавать программы,
712
    работающие и под DOS и под windows.
7544 leency 713
Return to contents.
7496 leency 714
 
715
 
7544 leency 716

7496 leency 717
    2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции.
7544 leency 718
7496 leency 719
 
720
        Секция .bss создается автоматически при компиляции программ с ключом
721
    /w32. Если Вы хотите иметь эту секцию и при компиляции программ с
722
    ключами /w32c или /dll Вам необходимо добавить либо в командной
723
    строке опцию /wbss, либо строку wbss в INI-файле, либо директиву
724
    #pragma option wbss.
725
 
726
        Использование секции .bss практически не влияет на размер получаемого
727
    файла. Теоретически, для процессоров, у которых есть отдельный кэш для
728
    данных, использование секции .bss, должно повышать скорость работы
729
    программы.
7544 leency 730
Return to contents.
7496 leency 731
 
732
 
7544 leency 733

7496 leency 734
    2.1.11 /DBG - создание отладочной информации.
7544 leency 735
7496 leency 736
 
737
        Если при компиляции программы в командную строку добавить ключ /dbg,
738
    или в файл конфигурации c--.ini добавить строку dbg, то компилятор после
739
    окончания компиляции создаст файл с отладочной информацией. Этот файл
740
    имеет имя главного модуля и имеет расширение *.tds.
741
 
742
        Отладочная информация создаваемая компилятором C-- совместима с
743
    отладочной информацией создаваемой компиляторами фирмы Borland. Но, пока,
744
    эта информация реализована еще не в полном объеме. Создаваемой сейчас
745
    отладочной информации достаточно для проведения простейшей отладки
746
    программы.
747
 
748
        Для 16-битных программ под DOS для отладки надо использовать Turbo
749
    Debugger из пакета Borland C v4.5 или лучше (файл td.exe).
750
 
751
        Для программ под Windows надо использовать 32-битный отладчик из этого
752
    же пакета (файл td32.exe).
753
 
754
        Для 32-битных программ, использующих расширитель DOS применять для
755
    отладки Turbo Debugger невозможно. Но, может быть я не знаю, как это
756
    делать. Если Вы знаете, как создавать 32-битные программы с
757
    DOS-расширителем компиляторами фирмы Borland с включением в них отладочной
758
    информации, то расскажите мне. А я попробую применить это для C--.
7544 leency 759
Return to contents.
7496 leency 760
 
761
 
7544 leency 762

7496 leency 763
    2.1.12 /J0 /J1 /J2
7544 leency 764
7496 leency 765
 
766
        Синонимом ключей /J0 /J1 /J2 является директива #jumptomain с
767
    параметрами NONE, SHORT и NEAR соответственно.
768
 
769
        Директива #jumptomain выполняет немного различные функции в
770
    зависимости от типа выходного файла.
771
 
772
        Компиляция файла типа *.com и *.exe модель памяти tiny:
773
 
774
      #jumptomain NONE (-j0) - в этом случае по окончании кода начальной
775
    инициализации программы не генерируется jmp на процедуру main. Эту
776
    директиву следует использовать в случае, если до процедуры main нет других
777
    не динамических процедур и инициализированных переменных.
778
 
779
      #jumptomain SHORT (-j1) - в этом случае по окончании кода начальной
780
    инициализации генерируется короткий jmp на процедуру main. Эту директиву
781
    следует использовать, если до процедуры main находится не более 128 байт
782
    кода и данных.
783
 
784
      #jumptomain NEAR (-j2) - это состояние устанавливается по умолчанию. При
785
    этом генерируется близкий jmp на процедуру main.
786
 
787
        Компиляция файлов *.exe (ключи -exe -d32 -w32 -w32c):
788
 
789
      #jumptomain NONE (-j0) - в этом случае код начальной инициализации
790
    программы не генерируется и управление при запуске передается сразу на
791
    процедуру main.
792
 
793
      Во всех остальных случаях генерируется код начальной инициализации и
794
    управление на процедуру main передается инструкцией call.
795
 
796
        Компиляция файлов *.dll:
797
 
798
      #jumptomain NONE (-j0) - в этом случае код начальной инициализации
799
    программы не генерируется и управление при запуске передается сразу на
800
    процедуру main.
801
 
802
      Во всех остальных случаях генерируется код заглушки и управление на
803
    процедуру main не передается. Фактически процедура main в этом случае не
804
    нужна.
805
 
806
      Процедура main при создании файлов DLL должна выглядеть немного иначе,
807
    чем в других случаях:
808
 
809
    dword main ( dword hInstDLL, reason, reserv )
810
    {
811
      ...
812
    }
7544 leency 813
Return to contents.
7496 leency 814
 
815
 
7544 leency 816

7496 leency 817
    2.1.13 /LST - Создание ассемблерного листинга.
7544 leency 818
7496 leency 819
 
820
        С помощью дополнительной опции командной строки -lst Вы можете
821
    получить вместе с исполнительным файлом и его ассемблерный листинг.
822
    Листинг будет помещен в файл одноименный с исполнительным файлом и
823
    имеющим расширение *.lst.
824
 
825
        Ассемблерный листинг создается независимой от компилятора частью кода
826
    с использованием информации накапливаемой при компиляции программы.
7544 leency 827
Return to contents.
7496 leency 828
 
829
 
7544 leency 830

7496 leency 831
    2.1.14 /ENV - Сохранение адреса переменных окружения.
7544 leency 832
7496 leency 833
 
834
        Если при компиляции программы Вы в командную строку добавите опцию
835
    -ENV или в файл c--.ini строка ENV, то компилятор добавит в вашу
836
    программу переменную environ, в которой при загрузке будет сохранятся
837
    адрес переменных окружения запускаемой программы. Для программ под
838
    Windows это будет полный адрес, а для остальных в этой переменной будет
839
    сохраняться только адрес сегмента.
7544 leency 840
Return to contents.
7496 leency 841
 
842
 
7544 leency 843

7496 leency 844
    2.1.15 /CPA - Очистка post-области данных.
7544 leency 845
7496 leency 846
 
847
        Переменные, которым в теле программы не было присвоено никакое
848
    значение, не включаются в тело скомпилированной программы. Для них
849
    резервируется память за пределами программы. Но эта память может быть
850
    заполнена произвольной информацией.
851
 
852
        Если Вам необходимо, чтобы неинициализированные переменные при
853
    загрузке программы всегда содержали одно и тоже значение (ноль) -
854
    включите в командную строку опцию -CPA.
7544 leency 855
Return to contents.
7496 leency 856
 
857
 
7544 leency 858

7496 leency 859
    2.1.16 /W - вывод предупреждений.
7544 leency 860
7496 leency 861
 
862
        По умолчанию компилятор не выводит предупреждения и многие даже не
863
    подозревают о существовании такой полезной опции. В C-- предупреждения
864
    фактически являются подсказками для создания оптимальных программ и
865
    зачастую облегчают отладку программ. В предупреждениях компилятор может
866
    сообщить Вам о том, в каком месте можно использовать короткие формы
867
    операторов IF, WHILE, FOR... О том, какие процедуры, переменные и
868
    структуры определенные в вашей программе не были использованы. О том
869
    какие регистры компилятор использовал без вашего ведома и много другой
870
    полезной информации.
871
 
872
        По умолчанию предупреждения выводятся на экран. Но их бывает так
873
    много, что они могут не поместиться на экране. Поэтому в компиляторе есть
874
    опция, по которой все предупреждения выводятся в файл. Имя этого файла
875
    задается в той же опции. Поместив в свой c--.ini файл пару вот этих строк:
876
 
877
    w
878
    wf=warning
879
 
880
        Вы будете получать в файле warning предупреждения.
7544 leency 881
Return to contents.
7496 leency 882
 
883
 
7544 leency 884

7496 leency 885
    2.1.17 /NW - Выборочное отключение типов предупреждений.
7544 leency 886
7496 leency 887
 
888
        Сейчас компилятор может выдавать 12 типов предупреждений и, иногда их
889
    бывает так много, что становится трудно в них ориентироваться. Теперь
890
    можно выборочно запрещать выдачу предупреждений. Для этого в командной
891
    строке (или в файле C--.INI) можно установить опцию /nw=number, где
892
    number - число от 1 до 12. Этим цифрам соответствуют следующие типы
893
    предупреждений:
894
 
895
      1 - Optimize numerical expressions
896
      2 - Compiler used register ..."
897
      3 - Short operator '...' may be used
898
      4 - String '...' repeated
899
      5 - Expansion variable
900
      6 - Signed value returned
901
      7 - '...' defined above, therefore skipped.
902
      8 - Variable/structure/procedure '...' possible not used
903
      9 - Non-initialized variable may have been used
904
     10 - Return flag was destroyed
905
     11 - Code may not be executable
906
     12 - Don't use local/parametric values in inline procedures
7544 leency 907
Return to contents.
7496 leency 908
 
909
 
7544 leency 910

7496 leency 911
    2.1.18 /WSI - короткая таблица импорта.
7544 leency 912
7496 leency 913
 
914
        Таблица импорта обычно состоит в свою очередь из четырех таблиц. Две
915
    таблицы LookUp Table и Import Address Table абсолютно одинаковы.
916
 
917
        Опцией командной строки /WSI Вы можете заставить компилятор
918
    генерировать только одну из этих двух одинаковых таблиц (генерируется
919
    только Import Address Table). Тем самым у Вас получится более компактная
920
    таблица импорта, что приведет, в некоторых случаях, к созданию более
921
    компактного выходного файла.
7544 leency 922
Return to contents.
7496 leency 923
 
924
 
7544 leency 925

7496 leency 926
  2.2 Директивы транслятора.
7544 leency 927
7496 leency 928
 
929
      C-- не содержит препроцессор. Тем не менее, есть несколько функций
930
  очень похожих на функции C препроцессора.
931
 
932
      Они даются как директивы транслятора. Все директивы транслятора
933
  начинаются с вопросительного знака ? либо с символа #. Вот список имеющихся
934
  директив и их назначение:
935
 
936
  ? align [val]                  Выровнять данные программы на четный по
937
                                 умолчанию или на адрес кратный величине val.
938
 
939
  ? aligncode [val]              Выровнять код программы на четный по
940
                                 умолчанию или на адрес кратный величине val.
7544 leency 941
                                 Заполнение производится кодом 0x90.
7496 leency 942
 
943
  ? aligner (aligner value)      определить значение байта вставки.
944
 
945
  ? alignword (TRUE or FALSE)    разрешает или запрещает выравнивание на
946
                                 четный адрес переменных типа word и int,
947
                                 значение по умолчанию TRUE.
948
 
949
  ? argc (TRUE or FALSE)         Включить или отключить альтернативный
950
                                 обработчик командной строки.
951
 
952
  ? atexit                       Вставляет в startup код поддержки процедуры
953
                                 ATEXIT().
954
 
955
  ? code32 (TRUE/FALSE)          разрешает/запрещает генерацию 32-битного
956
                                 кода.
957
 
958
  ? codesize                     оптимизация размера кода (в ущерб скорости).
959
 
960
  ? compilerversion min-vers     указывает, компилятор какой версии необходим
961
                                 для компиляции данной программы.
962
 
963
  ? ctrl_c (TRUE or FALSE )      разрешает или запрещает игнорирование
964
                                 нажатия CTRL-C.
965
 
966
  ? dataseg (value)              указывает компилятору сегментный адрес ОЗУ
967
                                 для переменных при компиляции ROM-BIOS.
968
 
969
  ? define (identifier) (token)  определяет идентификатор.
970
 
971
  ? DOSrequired (номер)          устанавливает минимальную требуемую версию
972
                                 DOS:  старший байт - номер версии,
973
                                 младший байт - номер модификации:
974
                                  0x0101 для версии 1.1 DOS
975
                                  0x0315 для версии 3.21 DOS
976
                                  0x0303 для версии 3.3 DOS
977
                                  0x0600 для версии 6.0 DOS
978
                                  0x0602 для версии 6.2 DOS и т.д.
979
 
980
  ? dosstring (TRUE/FALSE)       указывает компилятору, что в качестве
981
                                 терминатора строки надо использовать символ $
982
 
983
  ? else                         генерирует альтернативный код если ?ifdef или
984
                                 ?ifndef принимают значение FALSE (пример
985
                                 использования смотрите в файле FPU.H--)
986
 
987
  ? endif                        указывает на конец действия директив ifdef и
988
                                 ifndef
989
 
990
  ? fastcallapi (FALSE/TRUE)     запретить/разрешить генерацию быстрого вызова
991
                                 API-процедур (по умолчанию разрешено).
992
                                 Директива работает при компиляции программ
993
                                 под Windows.
994
 
995
  ? fixuptable (TRUE/FALSE)      разрешить/запретить создание FixUp таблицы
996
                                 (по умолчанию запрещено). Директива работает
997
                                 при компиляции программ под Windows.
998
 
999
  ? ifdef (identifier)           если идентификатор определен, то возвращает
1000
                                 TRUE иначе FALSE
1001
 
1002
  ? imagebase value              задает адрес Image Base. По умолчанию этот
1003
                                 адрес  равен 0x400000. Директива работает при
1004
                                 компиляции программ под Windows.
1005
 
1006
  ? ifndef (identifier)          если идентификатор определен, то возвращает
1007
                                 FALSE иначе TRUE
1008
 
1009
  ? include ("filename")         включает другой файл.
1010
 
1011
  ? includepath ("path")         указание компилятору, в какой директории надо
1012
                                 искать включаемые файлы
1013
 
1014
  ? initallvar                   инициализирует 0 все неинициализированные
1015
                                 переменные.
1016
 
1017
  ? jumptomain (NONE, SHORT, NEAR or FALSE)
1018
                                 устанавливает тип перехода к main(),
1019
                                 значение по умолчанию - NEAR.
1020
 
1021
  ? maxerrors (number)           максимальное количество найденных ошибок,
1022
                                 превысив которое транслятор прекращает
1023
                                 работу, значение по умолчанию - 16.
1024
 
1025
  ? movedatarom  (TRUE/FALSE)    указывает компилятору о необходимости
1026
                                 переноса данных из ПЗУ в ОЗУ.
1027
 
1028
  ? parsecommandline (TRUE or FALSE)
1029
                                 включает в программу блок кода для
1030
                                 синтаксического анализа командной строки
1031
                                 значение по умолчанию FALSE.
1032
 
1033
  ? pragma                       может объявлять несколько других директив
1034
 
1035
  ? print (number or string)     выводит на экран строку или число.
1036
 
1037
  ? printhex (number)            выводит на экран число в шестнадцатеричном
1038
                                 коде.
1039
 
1040
  ? randombyte                   вставляет в код программы байт случайного
1041
                                 значения.
1042
 
1043
  ? resize (TRUE or FALSE)       включает функцию изменения после запуска
1044
                                 размера выделенного программе блока памяти
1045
                                 на минимально требуемый объем,
1046
                                 значение по умолчанию TRUE.
1047
 
1048
  ? resizemessage (string)       сообщение, выводимое на экран перед
1049
                                 аварийным прерыванием выполнения программы,
1050
                                 если изменение размера выделенного программе
1051
                                 блока памяти не выполнено.
1052
 
1053
  ? setdinproc                   по этой директиве компилятор немедленно
1054
                                 вставляет в код компилируемой программы все
1055
                                 вызывавшиеся ранее динамические процедуры.
1056
 
1057
  ? sizerom  (value)             указывает компилятору размер ПЗУ.
1058
 
1059
  ? speed                        оптимизация быстродействия (значение
1060
                                 по умолчанию) в ущерб размеру кода.
1061
 
1062
  ? stack (number)               определяет размер стека программы в байтах.
1063
 
1064
  ? startaddress (number)        устанавливает стартовый адрес начала кода,
1065
                                 значение по умолчанию 0x100.
1066
 
1067
  ? startuptomain                в com-файлах размещает startup-код в
1068
                                 процедуре main().
1069
 
1070
  ? startusevar (number)         указывает адрес, с которого разрешено
1071
                                 использовать ячейки памяти под
1072
                                 неинициализированные переменные.
1073
 
1074
  ? sysattribute (значение)      эта директива передает компилятору атрибут
1075
                                 создаваемого драйвера. По умолчанию
1076
                                 устанавливается значение 0x2000.
1077
                                 Действует только с ключом /SYS.
1078
 
1079
  ? sysname <текстовая строка>   эта директива передает компилятору имя
1080
                                 будущего драйвера. По умолчанию
1081
                                 присваивается имя NO_NAME. Длина имени не
1082
                                 более 8 символов.  Действует только с ключом
1083
                                 /SYS.
1084
 
1085
  ? syscommand ,, ...; - эта директива
1086
                                 является обязательной при создании
1087
                                 драйверов. По этой директиве компилятору
1088
                                 передается список имен процедур обработки
1089
                                 команд драйвера. Действует только с ключом
1090
                                 /SYS.
1091
 
1092
  ? warning (TRUE or FALSE)      эта директива разрешает или запрещает выдачу
1093
                                 предупреждений. Директива действует только в
1094
                                 пределах текущего файла и не влияет на
1095
                                 включаемые файлы.
1096
 
1097
  ? winmonoblock FALSE           запрещает размещение таблиц файла формата PE
1098
                                 в одну секцию.
1099
 
1100
  ? undef                        уничтожает константы объявленные директивой
1101
                                 ? define
1102
 
1103
  ? use8086                      ограничивается при генерации объектного кода
1104
                                 командами 8088/8086 (значение по умолчанию).
1105
 
1106
  ? use8088                      ограничивается при генерации объектного кода
1107
                                 командами 8088/8086 (значение по умолчанию).
1108
 
1109
  ? use80186                     допускает при генерации объектного кода
1110
                                 команды и оптимизацию для процессора 80186.
1111
 
1112
  ? use80286                     допускает при генерации объектного кода
1113
                                 команды и оптимизацию для процессора 80286.
1114
 
1115
  ? use80386                     допускает при генерации объектного кода
1116
                                 команды и оптимизацию для процессора 80386.
1117
 
1118
  ? use80486                     допускает при генерации объектного кода
1119
                                 команды и оптимизацию для процессора 80486.
1120
 
1121
  ? usePentium                   допускает при генерации объектного кода
1122
                                 команды и оптимизацию для процессора Pentium.
1123
 
1124
  ? useMMX                       допускает при генерации объектного кода
1125
                                 команды и оптимизацию для процессора Pentium
1126
                                 MMX.
1127
 
1128
  ? usestartup                   разрешает компилятору использовать ячейки
1129
                                 памяти, занимаемые кодом начальной
1130
                                 инициализации программы.
7544 leency 1131
Return to contents.
7496 leency 1132
 
1133
 
7544 leency 1134

7496 leency 1135
    2.2.1 ?ifdef/?ifndef
7544 leency 1136
7496 leency 1137
 
1138
         Ранее директива ?ifdef срабатывала на наличие константы независимо
1139
    от значения ее величины, а директива ?ifndef срабатывала на отсутствие
1140
    константы в компилируемом файле. Теперь ?indef срабатывает лишь на
1141
    константу отличную от FALSE, а ?ifndef срабатывает как на отсутствие
1142
    константы в компилируемом файле, так и на константу имеющую значение
1143
    FALSE.
1144
 
1145
        Для директив ?ifdef/?ifndef зарезервированы константы codesize и
1146
    speed, которые принимают значение TRUE или FALSE в зависимости от режима
1147
    оптимизации. Это будет полезным для создания более гибких библиотек.
1148
 
1149
        Есть возможность проверки типа CPU для которого ведется компиляция.
1150
    Допустимые варианты синтаксиса:
1151
 
7544 leency 1152
    ?ifdef cpu > 1       //если программа компилируется для CPU выше 80186
7496 leency 1153
    ?ifndef cpu >= 2 // -------//------------- не больше или равно 80286
1154
    ?ifdef cpu == 3  // -------//------------- равно 80386
1155
    ?ifdef cpu != 0  // -------//------------- не равен 8086
1156
    ?ifdef cpu < 3   // -------//------------- хуже 80386
1157
    ?ifdef cpu <= 2  // -------//------------- хуже или равен 80286
1158
 
1159
        Эта директива позволит Вам писать одну процедуру для различных типов
1160
    CPU.
7544 leency 1161
Return to contents.
7496 leency 1162
 
1163
 
7544 leency 1164

7496 leency 1165
    2.2.2 ?initallvar
7544 leency 1166
7496 leency 1167
 
1168
        Директивой ?initallvar TRUE включается режим при котором всем
1169
    неинициализированным переменным будет присвоено нулевое значение и они
1170
    будут располагаться в том месте, где были объявлены. Т.е. практически
1171
    исчезнут неинициализированные переменные. Это может быть полезным при
1172
    написании драйверов и резидентных программ.
1173
 
1174
        Параметр FALSE этой директивы отключает этот режим.
1175
        По умолчанию эта директива установлена в FALSE.
7544 leency 1176
Return to contents.
7496 leency 1177
 
1178
 
7544 leency 1179

7496 leency 1180
    2.2.3 ?usestartup
7544 leency 1181
7496 leency 1182
 
1183
        Директива ?usestartup разрешает компилятору использовать ячейки кода
1184
    начальной инициализации программы (startup) для последующего размещения в
1185
    них неинициализированных переменных. Это может быть полезным для получения
1186
    более компактного кода, как обычных программ, так и в особенности
1187
    резидентных.
1188
 
1189
        Эту директиву применяют только для генерации *.COM файлов.
7544 leency 1190
Return to contents.
7496 leency 1191
 
1192
 
7544 leency 1193

7496 leency 1194
    2.2.4 ?startusevar
7544 leency 1195
7496 leency 1196
 
1197
        Директивой ?startusevar можно указать начальный адрес с которого
1198
    компилятор будет распределять память для неинициализированных переменных.
1199
    Например, получив директиву ?startusevar 0x53 компилятор будет
1200
    располагать неинициализированные переменные, начиная с адреса 0x53. Это
1201
    может быть полезным для получения более компактного кода как для
1202
    резидентных, так и для обычных программ.
1203
 
1204
        Эту директиву применяют только для генерации *.COM файлов.
7544 leency 1205
Return to contents.
7496 leency 1206
 
1207
 
7544 leency 1208

7496 leency 1209
    2.2.5 ?atexit
7544 leency 1210
7496 leency 1211
 
1212
        Директива ?atexit добавляет в startup программы код поддержки
1213
    процедуры ATEXIT, резервирует место для хранения 16 адресов процедур и
1214
    изменяет код процедур ABORT и EXIT.
1215
 
1216
        Процедура ATEXIT регистрирует процедуру, адрес которой передается ей в
1217
    качестве параметра, как процедуру завершения программы. Эта процедура
1218
    будет вызвана в момент завершения программы процедурами ABORT или EXIT
1219
    или инструкцией RET из main.
1220
 
1221
        Всего можно зарегистрировать до 16 процедур. Процедуры вызываются в
1222
    порядке обратном порядку их регистрации.
7544 leency 1223
Return to contents.
7496 leency 1224
 
1225
 
7544 leency 1226

7496 leency 1227
    2.2.6 ?startuptomain
7544 leency 1228
7496 leency 1229
 
1230
        По этой директиве компилятор в начале файла делает jmp на начало
1231
    процедуры main(). Перед началом компиляции этой процедуры компилятор
1232
    начнет компиляцию startup кода и лишь затем будет продолжена компиляция
1233
    процедуры main(). Тем самым startup код окажется не в начале файла, как
1234
    это происходит обычно, а в теле процедуры main(). Это будет полезным при
1235
    компиляции резидентных программ (TSR).
1236
 
1237
        Директива ?startuptomain работает только при компиляции com-файлов.
7544 leency 1238
Return to contents.
7496 leency 1239
 
1240
 
7544 leency 1241

7496 leency 1242
    2.2.7 ?undef
7544 leency 1243
7496 leency 1244
 
1245
        Эта директива уничтожает константы объявленные директивой ?define. Ее
1246
    можно применять для изменения в процессе компиляции значения какой-нибудь
1247
    константы.
7544 leency 1248
Return to contents.
7496 leency 1249
 
1250
 
7544 leency 1251

7496 leency 1252
    2.2.8 ?align и ?aligncode
7544 leency 1253
7496 leency 1254
 
1255
        В C-- существует директива ?align, которая делает однократное
1256
    выравнивание данных на четный адрес. Но если к этой директиве добавить
1257
    число, то выравнивание будет произведено на адрес кратный этому числу.
1258
    Например директива ?align 4 дополнит сегмент данных до адреса кратного
1259
    4. При выравнивании будут вставляться байты, значения которых определяются
1260
    директивой ?aligner, по умолчанию это значение равно нулю. Директива
1261
    ?align производит выравнивание только в сегменте данных. В тех моделях
1262
    памяти, в которых сегмент данных и кода совпадают эту директиву можно
1263
    применять и для выравнивания начала процедур.
1264
 
1265
        Директива ?aligncode [value] делает выравнивание в сегменте кода на
1266
    адрес кратный значению value, по умолчанию на четный адрес. Значение байта
1267
    заполнения в этой директиве является число 0x90 - код инструкции NOP.
1268
    Значение байта заполнения для этой директивы изменить нельзя. Т.о. эту
1269
    директиву можно применять и внутри исполняемого кода. Например, если Вы
1270
    хотите получить быстрый код на 486 процессоре, то рекомендуется делать
1271
    выравнивание начала процедур и циклов на адрес кратный 16.
7544 leency 1272
Return to contents.
7496 leency 1273
 
1274
 
7544 leency 1275

7496 leency 1276
    2.2.9 ?pragma
7544 leency 1277
7496 leency 1278
 
1279
        Директива #pragma это многофункциональнальная директива, которая в
1280
    свою очередь имеет свои директивы:
1281
 
1282
      option
1283
        Директива option позволяет включить в Ваш код опции командной строки
1284
    компилятора. Некоторые опции не могут быть использованы в этой директиве;
1285
    другие должны помещаться в самом начале исходного текста. Пример:
1286
 
1287
      #pragma option w32c
1288
 
1289
        Эта директива объявляет компилятору, что надо создать консольный
1290
    32-битный файл под windows.
1291
 
1292
      startup
1293
       Директивой startup можно указать функцию, которая будет выполнена перед
1294
    запуском процедуры main. Эта директива имеет такой формат:
1295
 
1296
      #pragma startup procname
1297
 
1298
        Количество раз, которое можно применять эту директиву в одной
1299
    программе не ограничено, но реально можно использовать лишь несколько
1300
    тысяч раз.
1301
 
1302
      line
1303
        Директива line выводит на экран номер текущей строки и имя файла.
1304
    Дополнительно может выводиться содержимое строки находящееся после слова
1305
    line. Пример:
1306
 
1307
      #pragma line information
1308
 
1309
        Встретив эту директиву, компилятор выведет на экран номер строки и имя
1310
    файла. Также будет выведено сообщение справа от слова line, если оно
1311
    есть.
1312
 
1313
      resource
1314
        Эта директива может принимать значения start и end. Эти два
1315
    значения выделяют начало и конец блока ресурсов, если вы используете его
1316
    непосредственно в исходном коде файла, а не в отдельном файле. Пример:
1317
 
1318
    #pragma resource start
1319
 
1320
    MyMenu MENU DISCARDABLE
1321
    BEGIN    POPUP "Files",HELP
1322
        BEGIN
1323
            MENUITEM "Open",                        ID_OPEN
1324
            MENUITEM "Save",                        ID_SAVE
1325
            MENUITEM SEPARATOR
1326
            MENUITEM "Exit",                        ID_EXIT
1327
        END
1328
        MENUITEM "Other",                           65535
1329
    END
1330
 
1331
    #pragma resource end
7544 leency 1332
Return to contents.
7496 leency 1333
 
1334
 
7544 leency 1335

7496 leency 1336
3. Константы.
1337
 
1338
  3.1 Числовые константы.
7544 leency 1339
7496 leency 1340
 
1341
      Представление числовых констант в виде десятичных чисел (чисел с
1342
  основанием 10) и шестнадцатеричных чисел (основание счисления 16) полностью
1343
  аналогично языку C.
1344
 
1345
      При двоичном представлении чисел (основание 2) число должно начинаться
1346
  с символов 0b, за которыми без пробела идет последовательность нулей и
1347
  единиц.
1348
 
1349
      При восьмеричном представлении чисел (основание 8) число должно
1350
  начинаться с символов 0o, за которыми без пробела идет последовательность
1351
  цифр.
1352
 
1353
      Вещественное число отличается от целого по наличию в нем точки.
1354
  Начинаться вещественное число должно либо цифрой от 0 до 9, либо знаком
1355
  минус. Необязательной частью вещественного числа является показатель
1356
  степени. Показатель степени отделяется от числа символом e или E.
1357
  Пробелы недопустимы.
1358
 
1359
  Примеры:
1360
    0b11111111 // двоичное представление числа 255
1361
    0x00F // шестнадцатеричное представление числа 15
1362
    0o10 // восьмеричное представление числа 8
1363
    1.234567E-20 // вещественное число
1364
 
1365
      C-- вместе с традиционным C-стилем шестнадцатеричных чисел понимает и
1366
  числа записанные в стиле ассемблера. Для тех, кто вдруг не знает, сообщаю,
1367
  что шестнадцатеричные числа в ассемблере имеют на конце символ h или H.
1368
  Если первый символ шестнадцатеричного числа больше 9, то перед ним
1369
  обязательно должен быть записан символ 0. Примеры:
1370
 
1371
    1234h
1372
    0A000H
1373
 
1374
      К числовым константам можно писать суффиксы L, U и F. Фактически
1375
  эти суффиксы в C-- не играют никакой роли, компилятор их просто
1376
  проглатывает. Пример:
1377
 
1378
  #define DEF  1L
1379
  #define DEF2 2Lu
1380
  #define DEF3 3.0F
1381
 
1382
      Эти суффиксы не зависят от регистра, т.е. их можно писать как
1383
  маленькими, так и большими буквами.
7544 leency 1384
Return to contents.
7496 leency 1385
 
1386
 
7544 leency 1387

7496 leency 1388
  3.2 Символьные константы.
7544 leency 1389
7496 leency 1390
 
1391
      Одиночные символьные константы, как и в C, должны заключаться в
1392
  одиночные кавычки '.
1393
 
1394
      Также как и в C, для обозначения специальных символов служит обратная
1395
  наклонная черта вправо \ с последующим за ней ключевым символом (или
1396
  несколькими символами). Поддерживаются следующие специальные символы:
1397
 
1398
    \a  /* звуковой сигнал */
1399
    \b  /* забой */
1400
    \f  /* перевод  страницы */
1401
    \l  /* перевод строки */
1402
    \n  /* возврат каретки*/
1403
    \r  /* возврат каретки*/
1404
    \t  /* табуляция */
1405
    \x??  /* символ ASCII, соответствующий байтовому представлению,
1406
               состоящему из двух шестнадцатеричных цифр, расположенных
1407
               на месте знаков вопроса */
1408
     \???  /* символ ASCII, соответствующий байтовому представлению,
1409
               состоящему из трех десятичных цифр, расположенных
1410
               на месте знаков вопроса */
1411
 
1412
      Любой другой символ после обратной наклонной черты вправо будет принят
1413
  как простой символ.
1414
 
1415
      Символ "Одиночная кавычка" ' может быть введен при помощи конструкции
1416
  \'
1417
 
1418
      Символ NULL может быть введен как ''
1419
 
1420
      В C-- поддерживаются и многобуквенные символьные константы. Примеры
1421
  многобуквенных символьных констант:
1422
 
1423
         'ab'
1424
         'the'
1425
         'this is large'
1426
 
1427
      Никакого ограничения на число символов в символьной константе не
1428
  накладывается, но различаются только последние 4 символа. Это - максимум,
1429
  который может быть сохранен в 32-разрядной переменной. Например, константы
1430
  this is large и arge - одинаковы.
1431
 
1432
      C-- обрабатывает все символьные константы как числовые значения ASCII
1433
  символов. Для многобуквенных символьных констант первый символ
1434
  соответствует старшим разрядам, таким образом, значение для ab будет
1435
  закодировано как a*256+b.
7544 leency 1436
Return to contents.
7496 leency 1437
 
1438
 
7544 leency 1439

7496 leency 1440
  3.3 Строковые константы.
7544 leency 1441
7496 leency 1442
 
1443
      Строковые константы, как и в C, заключаются в двойные кавычки (").
1444
  Специальные символы внутри строк обозначаются так же, как и в символьных
1445
  константах. Все специальные символы имеют то же значение, что и в
1446
  символьных константах за исключением \n, который имеет значение новая
1447
  строка и заменяет собой пару символов возврат каретки и перевод
1448
  строки.
1449
 
1450
      В настоящее время наибольшая длина строковой константы - 2048 символов,
1451
  включая символ-ограничитель 0, таким образом, максимум 2047 значащих
1452
  символов.
7544 leency 1453
Return to contents.
7496 leency 1454
 
1455
 
7544 leency 1456

7496 leency 1457
  3.4 Постоянные выражения.
7544 leency 1458
7496 leency 1459
 
1460
      Постоянное выражение - одиночная числовая константа или несколько
1461
  числовых констант, связанных между собой операторами. Числовое значение
1462
  выражения вычисляется один раз во время компиляции и далее используется
1463
  только его постоянное значение.
1464
 
1465
      Подобно всем выражениям в C--, постоянные выражения всегда вычисляются
1466
  слева направо, невзирая на правила арифметики! Это совершенно отлично от
1467
  других языков, и при написании выражений надо быть осторожным и помнить,
1468
  что 2+3*2=10 а не 8.
1469
 
1470
      Некоторые примеры постоянных выражений:
1471
  45 & 1 + 3 // равняется 4
1472
  14 - 1 / 2 // равняется 6 (помните целочисленные значения)
1473
  1 * 2 * 3 / 2 + 4 // равняется 7
1474
      Примеры с применением вещественных чисел:
1475
  3.23*1.53+2.0E2 // равняется 204.9419
7544 leency 1476
Return to contents.
7496 leency 1477
 
1478
 
7544 leency 1479

7496 leency 1480
4. Выражения.
1481
 
1482
  4.1 Типы выражений.
7544 leency 1483
7496 leency 1484
 
1485
      Имеются три типа выражений в C--, не считая постоянных выражений. Это
1486
  выражения типа EAX/AX/AL, выражения типа неEAX/AX/AL и условные выражения.
1487
  Все C-- выражения вычисляются слева направо, независимо от старшинства
1488
  входящих в выражение математических операций.
7544 leency 1489
Return to contents.
7496 leency 1490
 
1491
 
7544 leency 1492

7496 leency 1493
  4.2 Выражения типа EAX/AX/AL.
7544 leency 1494
7496 leency 1495
 
1496
      Этот тип выражений применяется в случае, когда его результат может быть
1497
  сохранен в переменной в памяти или в регистре EAX или AX или AL.
1498
 
1499
      Если результат может быть сохранен в переменных типа byte или char,
1500
  используется нотация AL.
1501
 
1502
      Если результат может быть сохранен в переменных типа word или int,
1503
  используется нотация AX.
1504
 
1505
      Если результат может быть сохранен в переменных типа dword, long или
1506
  float, используется нотация EAX.
7544 leency 1507
Return to contents.
7496 leency 1508
 
1509
 
7544 leency 1510

7496 leency 1511
  4.3 Выражения использующие получатель при вычислении выражения.
7544 leency 1512
7496 leency 1513
 
1514
      Если в правой части выражения используется переменная являющаяся
1515
  одновременно и приемником, то такие выражения дают различные результаты в
1516
  зависимости от того является приемник регистром или переменной памяти. Это
1517
  связано с тем, что при вычислении выражения в переменную памяти, вычисление
1518
  производится сначала в регистр EAX/AX/AL, и лишь после окончания вычисления
1519
  результат будет записан в приемник. Если же приемником является регистр, то
1520
  его значение будет меняться после каждой операции вычисления. Пример:
1521
 
1522
  int var;
1523
    var = BX = 2;
1524
    var = 3 + var; // результатом будет 5
1525
    BX = 3 + BX;   // результатом будет 6
7544 leency 1526
Return to contents.
7496 leency 1527
 
1528
 
7544 leency 1529

7496 leency 1530
  4.4 Не - EAX/AX/AL выражения.
7544 leency 1531
7496 leency 1532
 
1533
      Этот тип выражений применяется в случае, когда его результат должен
1534
  быть сохранен в любом другом регистре, отличном от аккумулятора EAX, AX
1535
  или AL. В процессе вычисления выражения этого типа меняется только
1536
  содержимое указанного регистра-получателя, все другие регистры будут
1537
  сохранены. Если регистром-получателем служит байтовый регистр, а при
1538
  вычислении используются величины размером в слово, одновременно с записью в
1539
  младший байт может быть разрушено содержимое старшего байта
1540
  регистра-получателя.
1541
 
1542
      Это обстоятельство накладывает некоторые ограничения на операции и
1543
  операнды, допустимые в выражениях типа не EAX/AX/AL. Внутри выражений
1544
  байтового типа не допускается:
1545
 
1546
      - делать вызовы МАКРОКОМАНД,
1547
      - делать вызовы РЕГИСТРОВЫХ процедур
1548
      - делать вызовы СТЕКОВЫХ процедур
1549
 
1550
      Ранее в не-EAX/AX/AL выражениях было можно использовать лишь
1551
  операции: сложения, вычитания, XOR, OR, AND. Теперь для 16 и 32 битных
1552
  регистров почти все ограничения сняты. Но есть еще ограничения на регистры.
1553
  Например, если в выражении используется сдвиг на значение переменной, а
1554
  приемником являются регистры CX/ECX, то такое выражение компилятор не будет
1555
  компилировать:
1556
 
1557
     CX = var * SI * 3 * var >> 3;  //вызовет сообщение об ошибке
1558
 
1559
      Примечание:  для 8 битных не-AL выражений умножать можно только на
1560
  числа: 0, 1, 2, 4, 8, 16, 32, 64 и 128. Все эти ограничения связаны со
1561
  стремлением не разрушать другие регистры при использовании не-EAX/AX/AL
1562
  выражений.
7544 leency 1563
Return to contents.
7496 leency 1564
 
1565
 
7544 leency 1566

7496 leency 1567
  4.5 Условные выражения.
7544 leency 1568
7496 leency 1569
 
1570
      Условные выражения - выражения, результатом вычисления которых является
1571
  логическое значение да или нет, используемое в операторе if и циклах do {}
1572
  while, while, for.
1573
 
1574
      Имеются два типа условных выражений, простые и сложные.
1575
 
1576
      Возможно логическое объединение условий.
7544 leency 1577
Return to contents.
7496 leency 1578
 
1579
 
7544 leency 1580

7496 leency 1581
    4.5.1 Простые условные выражения.
7544 leency 1582
7496 leency 1583
 
1584
        Простые условные выражения - одиночная лексема или выражение, которое
1585
    примет значение да, если расчетное значение отлично от нуля, или значение
1586
    нет, если расчетное значение равно нулю.
7544 leency 1587
Return to contents.
7496 leency 1588
 
1589
 
7544 leency 1590

7496 leency 1591
    4.5.2 Сложные условные выражения.
7544 leency 1592
7496 leency 1593
 
1594
    Сложные условные выражения имеют следующую форму:
1595
 
1596
         (левая_часть оператор_отношения правая_часть)
1597
 
1598
    Где:
1599
       левая_часть - любое выражение типа AL/AX/EAX или постоянное выражение.
1600
                     Тип выражения определяется по типу первой лексемы
1601
                     (регистра или переменной); значение типа по умолчанию -
1602
                     word для 16-битных программ и dword для 32-битных. Если
1603
                     желателен другой тип, перед выражением ставится
1604
                     соответствующее ключевое слово, определяющее его тип:
1605
                     byte, char, int, long, dword или float
1606
 
1607
       оператор_отношения - любой из операторов отношения:
1608
                     ==, !=, <>, <, >, <=, или >=.
1609
 
1610
       правая_часть - любой одиночный регистр, одиночная переменная или
1611
                     постоянное выражение.
1612
 
1613
    Примеры правильных сложных условных выражений:
1614
 
1615
         (X + y > z)
1616
         (int CX*DX < = 12*3)
1617
         (byte first*second+hold == cnumber)
1618
 
1619
    Примеры недопустимых сложных условных выражений:
1620
 
1621
         (x+y >= x-y) // правая часть не является одиночной лексемой или
1622
                         постоянным выражением.
1623
         (Z = y) // вместо == ошибочно поставлен =
7544 leency 1624
Return to contents.
7496 leency 1625
 
1626
 
7544 leency 1627

7496 leency 1628
  4.6 Изменение типа выражения при присваивании.
7544 leency 1629
7496 leency 1630
 
1631
      Если после знака равенства написать тип отличный от типа вычисляемой
1632
  переменной, то все переменные участвующие в процессе вычисления, будут
1633
  преобразовываться к этому новому типу, и лишь конечный результат будет
1634
  преобразован к типу вычисляемой переменной. Пример:
1635
 
1636
  int i, a;
1637
  long b;
1638
  char c;
1639
 
1640
    i = a * b + c ;
1641
 
1642
      Значения переменных a, b, и c в этом примере перед вычислением будут
1643
  преобразованы к типу int (типу переменной i). Но если записать это
1644
  выражение вот так:
1645
 
1646
    i = long a * b + c ;
1647
 
1648
      то  переменные  a,  b,  и  c  в  этом  примере  перед  вычислением будут
1649
  преобразованы к типу  long, а конечный  результат будет преобразован  к типу
1650
  переменной i - int.
7544 leency 1651
Return to contents.
7496 leency 1652
 
1653
 
7544 leency 1654

7496 leency 1655
  4.7 Вычисление в регистры EAX/AX/AL со знаком.
7544 leency 1656
7496 leency 1657
 
1658
      По умолчанию все вычисления в регистры производятся как с без знаковыми
1659
  величинами.
1660
 
1661
  Например:
1662
 
1663
    int a,b,c;
1664
    AX = a * b / c ;
1665
 
1666
  При этом компилятор генерировал без знаковые инструкции div и mul, так как
1667
  регистры считаются без знаковыми переменными. Если написать вот так:
1668
 
1669
    AX = int a * b / c ;
1670
 
1671
  то компилятор сгенерирует инструкции idiv и imul.
1672
 
1673
       Обращаю ваше внимание, что для регистра AL можно использовать только
1674
  модификатор char, для AX соответственно только int, а для EAX - long. Для
1675
  остальных регистров подобное делать нельзя.
7544 leency 1676
Return to contents.
7496 leency 1677
 
1678
 
7544 leency 1679

7496 leency 1680
5. Идентификаторы.
1681
 
1682
  5.1 Формат идентификатора.
7544 leency 1683
7496 leency 1684
 
1685
      Идентификаторы в C-- должны начинаться или с символа подчеркивания _
1686
  или заглавных или строчных букв. Следующие символы могут быть любой
1687
  комбинацией символов подчеркивания, заглавных или строчных букв или чисел
1688
  (от 0 до 9). Общая длина идентификатора не может превышать 64 символа.
1689
  Символы с кодом больше 0x7A (код символа z) недопустимы.
1690
 
1691
  Примеры допустимых идентификаторов:
1692
 
1693
  _DOG
1694
  Loony12
1695
  HowdYBoys_AND_Girls
1696
  WOW___
1697
  X
1698
 
1699
  Примеры недопустимых идентификаторов:
1700
 
1701
  12bogus                                 /* не может начинаться с числа */
1702
  WowisthisalongidentifieryupitsureisnotOyoulengthismorethat64chars
1703
   /*длина идентификатора превышает 64 */
1704
  Y_es sir                                /* пробелы недопустимы */
1705
  The-end                                 /* дефисы недопустимы */
7544 leency 1706
Return to contents.
7496 leency 1707
 
1708
 
7544 leency 1709

7496 leency 1710
  5.2 Зарезервированные идентификаторы.
7544 leency 1711
7496 leency 1712
 
1713
      Список зарезервированных в C-- идентификаторов, которые не могут
1714
  использоваться как общие идентификаторы, поскольку они уже были определены
1715
  или зарезервированы для других целей:
1716
 
1717
  BREAK  CASE    CONTINUE  ELSE    EXTRACT  FALSE  FOR
1718
  FROM   GOTO    IF        LOOPNZ  RETURN   SWITCH TRUE
1719
  WHILE
1720
 
1721
  CARRYFLAG    MINUSFLAG  NOTCARRYFLAG  NOTOVERFLOW
1722
  NOTZEROFLAG  OVERFLOW   PLUSFLAG      ZEROFLAG
1723
 
1724
  __CODEPTR__ __COMPILER__ __DATAPTR__ __DATESTR__ __DATE__    __DAY__
1725
  __HOUR__    __LINE__     __MINUTE__  __MONTH__   __POSTPTR__ __SECOND__
1726
  __TIME__    __VER1__     __VER2__    __WEEKDAY__ __YEAR__
1727
 
1728
  _export  asm     break   byte      case     cdecl   char       continue
1729
  default  do      dword   else      enum     extern  far        fastcall
1730
  float    for     goto    if        inline   int     interrupt  long
1731
  loop     loopnz  pascal  return    short    signed  sizeof     static
1732
  stdcall  struct  switch  union     unsigned void    while      word
1733
 
1734
  ESCHAR  ESBYTE  ESINT  ESWORD  ESLONG  ESDWORD  ESFLOAT
1735
  CSCHAR  CSBYTE  CSINT  CSWORD  CSLONG  CSDWORD  CSFLOAT
1736
  SSCHAR  SSBYTE  SSINT  SSWORD  SSLONG  SSDWORD  SSFLOAT
1737
  DSCHAR  DSBYTE  DSINT  DSWORD  DSLONG  DSDWORD  DSFLOAT
1738
  FSCHAR  FSBYTE  FSINT  FSWORD  FSLONG  FSDWORD  FSFLOAT
1739
  GSCHAR  GSBYTE  GSINT  GSWORD  GSLONG  GSDWORD  GSFLOAT
1740
 
1741
  AX   CX   DX   BX   SP   BP   SI   DI
1742
  EAX  ECX  EDX  EBX  ESP  EBP  ESI  EDI
1743
  AL   CL   DL   BL   AH   CH   DH   BH
1744
  ES   CS   SS   DS   FS   GS
1745
 
1746
  ST(0)  ST(1)  ST(2)  ST(3)  ST(4)  ST(5)  ST(6)  ST(7)  ST
1747
  st(0)  st(1)  st(2)  st(3)  st(4)  st(5)  st(6)  st(7)  st
1748
1749
      Этот список может быть получен из C-- транслятора в любое время,
1750
  запуском его с опцией /WORDS из командной строки.
1751
 
1752
      Если Вы пользуетесь при компиляции опцией командной строки /ia, которая
1753
  позволяет использовать ассемблерные инструкции не заключая их в блоки asm и
1754
  без префикса $, то все имена ассемблерных инструкций становятся
1755
  зарезервированными словами. Причем имена ассемблерных инструкций компилятор
1756
  различает независимо от того, написаны они маленькими или большими буквами.
1757
 
1758
     Список имен поддерживаемых компилятором ассемблерных инструкции можно
1759
  получить запустив компилятор с опцией /LAI.
1760
 
1761
     Кроме этого в ассемблерных инструкциях становятся зарезервированными
1762
  следующие идентификаторы:
1763
 
1764
  ax   cx   dx   bx   sp   bp   si   di
1765
  eax  ecx  edx  ebx  esp  ebp  esi  edi
1766
  al   cl   dl   bl   ah   ch   dh   bh
1767
  es   cs   ss   ds   fs   gs
1768
 
1769
  DR0   DR1   DR2   DR3   DR4   DR5   DR6   DR7
1770
  CR0   CR1   CR2   CR3   CR4   CR5   CR6   CR7
1771
  TR0   TR1   TR2   TR3   TR4   TR5   TR6   TR7
1772
  MM0   MM1   MM2   MM3   MM4   MM5   MM6   MM7
1773
  XMM0  XMM1  XMM2  XMM3  XMM4  XMM5  XMM6  XMM7
1774
 
1775
  dr0   dr1   dr2   dr3   dr4   dr5   dr6   dr7
1776
  cr0   cr1   cr2   cr3   cr4   cr5   cr6   cr7
1777
  tr0   tr1   tr2   tr3   tr4   tr5   tr6   tr7
1778
  mm0   mm1   mm2   mm3   mm4   mm5   mm6   mm7
1779
  xmm0  xmm1  xmm2  xmm3  xmm4  xmm5  xmm6  xmm7
7544 leency 1780
Return to contents.
7496 leency 1781
 
1782
 
7544 leency 1783

7496 leency 1784
  5.3 Универсальные регистры для 16 и 32-битного режима.
7544 leency 1785
7496 leency 1786
 
1787
      При создании библиотечных процедур очень часто приходится писать
1788
  варианты процедуры для работы в 16-битном и 32-битном режимах, которые
1789
  отличаются друг от друга лишь использованием в них либо 16-битных либо
1790
  32-битных регистров соответственно. Но можно писать лишь одну процедуру,
1791
  используя в ней новый синтаксис регистров. Если компилятор встретит вот
1792
  такой синтаксис:
1793
 
1794
    (E)AX=0;
1795
 
1796
      то компилятор будет использовать при компиляции 16-битного кода регистр
1797
  AX, а при компиляции 32-битного кода регистр EAX.
1798
 
1799
      Использование автоматических регистров позволит упростить библиотечные
1800
  файлы и сделать их более понятными.
7544 leency 1801
Return to contents.
7496 leency 1802
 
1803
 
7544 leency 1804

7496 leency 1805
  5.4 Предопределенные идентификаторы.
7544 leency 1806
7496 leency 1807
 
1808
      Идентификаторы, определяемые компилятором в зависимости от режима
1809
  компиляции:
1810
 
1811
  __TLS__     идет компиляция под windows (w32, w32c, dll).
1812
  __DLL__     идет компиляция dll.
1813
  __CONSOLE__ идет компиляция консольного приложения windows
1814
  __WIN32__   идет компиляция GUI-шного приложения
1815
  __FLAT__    компилируется 32-битный код.
1816
  __MSDOS__   компилируется 16-битный код.
1817
  __TINY__    используется модель памяти tiny в 16-битном режиме
1818
  __SMALL__   используется модель памяти small в 16-битном режиме
1819
  __DOS32__   компилируется 32-битный код под DOS (d32)
1820
  __COM__     компилируется com-файл
1821
  __SYS__     компилируется sys-файл
1822
  __ROM__     компилируется rom-файл
1823
  __OBJ__     компилируется obj-файл
1824
  __TEXE__    компилируется exe-файл модели tiny
1825
  __EXE__     компилируется exe-файл модели small
1826
  __MEOS__    компилируется исполняемый файл для MenuetOS
1827
  codesize    компиляция ведется с оптимизацией на размер кода
1828
  speed       компиляция ведется с оптимизацией на быстродействие кода
1829
  cpu         определяет тип процессора для которого ведется компиляция:
7544 leency 1830
 
1831
            1 - 80186
1832
            2 - 80286
1833
            3 - 80386
1834
            4 - 80486
1835
            5 - Pentium
1836
            6 - Pentium MMX
1837
            7 - Pentium II
7496 leency 1838
 
1839
      Эти идентификаторы могут быть проверены директивами #ifdef или #ifndef.
1840
  Идентификатор cpu может быть использован лишь с операторами проверки
1841
  условий:
1842
 
1843
  #ifdef cpu > 3  //если тип процессора больше 80386
7544 leency 1844
Return to contents.
7496 leency 1845
 
1846
 
7544 leency 1847

7496 leency 1848
6. Переменные.
1849
 
1850
  6.1 Типы переменных.
7544 leency 1851
7496 leency 1852
 
1853
      В C-- имеется семь типов переменных (именованных областей памяти), это:
1854
  byte, word, dword, char, int, long, float.
1855
 
1856
      Следующая таблица  показывает размер  и диапазон  представляемых величин
1857
  каждого из типов переменной:
1858
 
1859
   NAME   | SIZE  |        VALUE RANGE          |        VALUE RANGE
1860
   тип    |размер |   диапазон представления    |   диапазон представления
1861
          |в байт.|    в десятичной системе     | в шестнадцатеричной системе
1862
  ---------------------------------------------------------------------------
1863
  byte    |   1   |           0 to 255          |        0x00 to 0xFF
1864
  word    |   2   |           0 to 65535        |      0x0000 to 0xFFFF
1865
  dword   |   4   |           0 to 4294967295   |  0x00000000 to 0xFFFFFFFF
1866
  char    |   1   |        -128 to 127          |        0x80 to 0x7F
1867
  int     |   2   |      -32768 to 32767        |      0x8000 to 0x7FFF
1868
  long    |   4   | -2147483648 to 2147483647   |  0x80000000 to 0x7FFFFFFF
1869
  float   |   4   |    -3,37E38 to +3,37E38     |  0xFF7FFFFF to 0x7FFFFFFF
1870
 
1871
      Примечание:  для работы с типами float, dword и long используются
1872
  32-разрядные целочисленные команды, следовательно, для их выполнения нужно
1873
  иметь процессор не хуже 80386, что сейчас не является большой проблемой.
1874
 
1875
      Для совместимости со стандартом, принятом в языке C, введены
1876
  новые зарезервированные слова: short, signed, unsigned. Для типа int
1877
  в 32-битном режиме изменена разрядность. Вот таблица всех вариантов новых
1878
  типов данных:
1879
 
1880
  ---------------------------------------------------------
1881
  |   полный тип     |допустимые сокращения|старые аналоги|
1882
  ---------------------------------------------------------
1883
  |signed char       |char                 |  char        |
1884
  |signed int        |signed, int          |  int/long    |
1885
  |signed short int  |short, signed short  |  int         |
1886
  |signed long int   |long, signed long    |  long        |
1887
  |unsigned char     |---                  |  byte        |
1888
  |unsigned int      |unsigned             |  word/dword  |
1889
  |unsigned short int|unsigned short       |  word        |
1890
  |unsigned long int |unsigned long        |  dword       |
1891
  ---------------------------------------------------------
1892
 
1893
      Старые типы byte, word и dword поддерживаются по прежнему и имеют
1894
  функционально прежнее значение. Изменения коснулись лишь типа int. Он в
1895
  16-битном режиме, также как и тип unsigned int, имеет 16-битный размер, а
1896
  в 32-битном режиме эти оба типа имеют размер в 32-бита. На первый взгляд
1897
  такие свойства типа int вносят некоторую путаницу, но это дает большой
1898
  выигрыш при использовании этого типа в библиотечных файлах, которые могут
1899
  быть использованы при компиляции 16-битных и 32-битных программ.
7544 leency 1900
Return to contents.
7496 leency 1901
 
1902
 
7544 leency 1903

7496 leency 1904
  6.2 Объявление переменных.
7544 leency 1905
7496 leency 1906
 
1907
      Синтаксис для объявления переменных следующий:
1908
 
1909
  variable-type identifier;
1910
 
1911
  где variable-type - char, byte, int, word, long, dword или float.
1912
 
1913
      Одновременно могут быть объявлены несколько идентификаторов одного типа:
1914
 
1915
  variable-type identifier1, identifier2, ... , identifierN;
1916
 
1917
      Одномерные массивы могут быть объявлены следующим образом:
1918
 
1919
  variable-type identifier[elements];
1920
 
1921
  где elements -  постоянное выражение для  количества переменных этого  типа,
1922
  объединенных в массив.
1923
 
1924
      Инициализированные массивы можно объявлять без указания числа
1925
  элементов. При этом будет создан массив по фактическому числу элементов.
1926
 
1927
  variable-type identifier[] = { const1, const2 };
1928
 
1929
      Переменные при объявлении могут быть проинициализированы следующим
1930
  образом:
1931
 
1932
  variable-type identifier = value;
1933
 
1934
      Некоторые примеры глобальных объявлений:
1935
  byte i,j;    /* объявляет две переменные типа byte с именами i и j */
1936
  word see[10] /* объявляет массив с именем see, состоящий из 10
1937
                  элементов типа word */
1938
  int h,x[27]  /* объявляет, переменную типа int с именем h,
1939
                  и массив с именем x, состоящий из 27 элементов типа int */
1940
  long size=0; /* объявлена переменная типа long с именем size и ей присвоено
1941
                  значение 0. */
7544 leency 1942
Return to contents.
7496 leency 1943
 
1944
 
7544 leency 1945

7496 leency 1946
  6.3 Глобальные переменные.
7544 leency 1947
7496 leency 1948
 
1949
      Глобальные переменные - это переменные, область действия которых
1950
  распространяется на всю программу. В C-- использовать глобальные переменные
1951
  можно в процедурах, расположенных ниже места ее объявления. Т.е. если Вы
1952
  пишите процедуру, в которой используете переменную var, а саму переменную
1953
  объявляете ниже текста процедуры, то компилятор выдаст ошибку. Это связано
1954
  с тем, что компилятор может знать тип переменной только после их
1955
  объявления.  Но для таких переменных можно использовать взятие их адреса,
1956
  так как адрес переменной не зависит от его типа. Пример:
1957
 
1958
  void Proc(){
1959
    gvar = 0; /* компилятор выдаст сообщение об ошибке, т.к. он еще не знает
1960
                 типа переменной gvar */
1961
    AX = #gvar; /* несмотря на то, что компилятор не знает и адреса этой
1962
                   переменной такое выражение будет откомпилировано */
1963
  }
1964
  int gvar;
1965
 
1966
      Но все же ситуация не безнадежна и нам удастся добиться того, чего мы
1967
  задумали. В этом нам поможет альтернативный синтаксис обращения к
1968
  переменным:
1969
 
1970
  void Proc(){
1971
    DSINT[#gvar] = 0; /* компилятор успешно откомпилирует это выражение т.к.
1972
                         ему теперь известен тип переменной gvar */
1973
  }
1974
  int gvar;
1975
 
1976
      Память под глобальные переменные выделяется в сегменте данных. Если
1977
  переменная при объявлении инициализируется (т.е. ей присвоено какое-то
1978
  значение), то переменная будет включена в код компилируемого файла. Если
1979
  переменная не инициализируется, то место для переменной будет
1980
  зарезервировано сразу же за последним байтом скомпилированной программы.
7544 leency 1981
Return to contents.
7496 leency 1982
 
1983
 
7544 leency 1984

7496 leency 1985
  6.4 Локальные переменные.
7544 leency 1986
7496 leency 1987
 
1988
      Локальные переменные - это переменные область действия которых
1989
  распространяется лишь в пределах одной процедуры. Объявлять локальные
1990
  переменные, в отличии от современных версий C, можно между именем процедуры
1991
  и первой открывающейся фигурной скобкой. Пример:
1992
 
1993
  void PROC ()
1994
  int i;  //объявлена локальная переменная типа int с именем i
1995
  {
1996
      for ( i=0; i<10; i++ ) WRITE(1);
1997
  }
1998
 
1999
      Память под локальные переменные отводится в сегменте стека.
2000
 
2001
      К локальным переменным можно отнести и параметры стековых процедур. Под
2002
  них также отводится память в стеке.
2003
 
2004
      Можно инициализировать локальные переменные при их объявлении. Но есть
2005
  некоторые ограничения. Нельзя инициализировать массивы и многомерные
2006
  структуры. Инициализировать можно одним значением, т.е нельзя при
2007
  инициализации локальных переменных пользоваться перечислением заключенным в
2008
  фигурные скобки и операторами FROM и EXTRACT.
2009
 
2010
      Имена локальных переменных могут совпадать с именами глобальных
2011
  переменных или процедур, но тогда Вы не сможете обратиться к глобальной
2012
  переменной или вызвать одноименную процедуру.
2013
 
2014
      Локальные переменные можно объявлять и в начале блока процедуры. Но
2015
  только до начала тела процедуры. Пример:
2016
 
2017
  void proc(){
2018
  int locproc;  // объявление локальной процедуры
2019
    locproc=0;  // а теперь пошло тело процедуры
2020
  int locproc;  // а на это объявление переменной компилятор выдаст сообщение
2021
                // об ошибке, т.к. уже началось тело процедуры
2022
  }
7544 leency 2023
Return to contents.
7496 leency 2024
 
2025
 
7544 leency 2026

7496 leency 2027
  6.5 Динамические переменные и структуры.
7544 leency 2028
7496 leency 2029
 
2030
      Наряду с уже известными Вам динамическими процедурами в C-- есть
2031
  возможность использовать динамически и переменные и структуры. Динамические
2032
  переменные и структуры обозначаются также как и динамические процедуры -
2033
  символом двоеточия перед началом их объявления. И также как и динамическая
2034
  процедура, динамическая переменная или структура будет вставлена в код,
2035
  лишь в том случае, если она будет использована в программе.
2036
 
2037
      Динамические переменные и структуры найдут применение в библиотеках.
2038
  Использовать их непосредственно в программах нет смысла.
2039
 
2040
      У динамических переменных, структур также как и у процедур, есть один
2041
  недостаток - Вы не сможете знать, в каком месте откомпилированного кода они
2042
  будут расположены, и в каком порядке. Но необходимость это знать бывает
2043
  очень редко.
2044
 
2045
      Динамические инициализированные переменные и структуры в файле будут
2046
  расположены в его самом конце, после динамических процедур. Эту их
2047
  особенность можно использовать, если Вам будет необходимо, чтобы данные не
2048
  были разбросаны среди кода, а были сгруппированы в одном месте.
7544 leency 2049
Return to contents.
7496 leency 2050
 
2051
 
7544 leency 2052

7496 leency 2053
  6.6 Присваивание одного значения нескольким переменным.
7544 leency 2054
7496 leency 2055
 
2056
      Если Вам необходимо присвоить нескольким переменным одинаковые значения:
2057
 
2058
    var1=0;
2059
    var2=0;
2060
    var3=0;
2061
 
2062
      то теперь это можно записать более коротко:
2063
 
2064
    var1=var2=var3=0;
2065
 
2066
      При использовании такой записи генерируется более компактный и более
2067
  быстрый код.
7544 leency 2068
Return to contents.
7496 leency 2069
 
2070
 
7544 leency 2071

7496 leency 2072
  6.7 Переменные типа float.
2073
 
2074
    6.7.1 Формат переменных типа float.
7544 leency 2075
7496 leency 2076
 
2077
        Для представления значений с плавающей точкой в язык C-- введен тип
2078
    float. Этому типу соответствует действительное число одинарной точности
2079
    FPU.
2080
 
2081
        Формат представления данных с плавающей точкой включает три поля:
2082
    знака, мантиссы и порядка. Знак определяется старшим значащим разрядом.
2083
    Поле мантиссы содержит значащие биты числа, а поле порядка содержит
2084
    степень 2 и определяет масштабирующий множитель для мантиссы.
2085
 
2086
    31 30.....23 22........0
2087
    |  |      |  |         |
2088
    |  |      |  -------------- - поле мантиссы
2089
    |  ------------------------ - поле порядка
2090
    --------------------------- - бит знака
7544 leency 2091
Return to contents.
7496 leency 2092
 
2093
 
7544 leency 2094

7496 leency 2095
    6.7.2 Константы с плавающей точкой.
7544 leency 2096
7496 leency 2097
 
2098
        Компилятор отличает вещественное число от целого по наличию в нем
2099
    точки. Начинаться вещественное число должно либо цифрой от 0 до 9, либо
2100
    знаком минус. Необязательной частью вещественного числа является
2101
    показатель степени. Показатель степени отделяется от числа символом e или
2102
    E. Пробелы недопустимы. Вот примеры допустимого синтаксиса:
2103
 
2104
     0.98
2105
     -15.75
2106
     3.14e2
2107
     1.234567E-20
7544 leency 2108
Return to contents.
7496 leency 2109
 
2110
 
7544 leency 2111

7496 leency 2112
    6.7.3 Диапазон допустимых значений.
7544 leency 2113
7496 leency 2114
 
2115
        Вещественное число типа float может находиться в диапазоне от 3.37E38
2116
    до -3.37E38. Минимально близкое к нулю значение равняется 1.17E-38 и
2117
    -1.17E-38. Записывать вещественное число одинарной точности более чем 8
2118
    цифрами не имеет смысла. Показатель степени может принимать значения от
2119
    +38 до -38.
7544 leency 2120
Return to contents.
7496 leency 2121
 
2122
 
7544 leency 2123

7496 leency 2124
    6.7.4 Математические операции.
7544 leency 2125
7496 leency 2126
 
2127
        Компилятор поддерживает 4 основных действия над переменными типа
2128
    float: сложение, вычитание, умножение и деление. Поддерживается также
2129
    инкремент (var++ - увеличение на 1), декремент (var-- - уменьшение на 1),
2130
    смена знака (-var) и обмен значениями (var1 >< var2). Остальные
2131
    математические операции будут реализованы либо уже реализованы во внешних
2132
    библиотеках. При вычислении значения переменной float можно использовать
2133
    и переменные других типов, они будут автоматически преобразованы в тип
2134
    float.
2135
 
2136
        ВНИМАНИЕ! Составные математические  операции выполняются в том
2137
    порядке, в котором они записаны, невзирая на правила арифметики.
7544 leency 2138
Return to contents.
7496 leency 2139
 
2140
 
7544 leency 2141

7496 leency 2142
    6.7.5 Преобразования типов.
7544 leency 2143
7496 leency 2144
 
2145
        При математических операциях конечным итогом которых является
2146
    переменная типа float, все операнды других типов перед вычислением будут
2147
    преобразованы в тип float. При присваивании переменной типа float значения
2148
    переменной другого типа оно также будет преобразовано в тип float.
2149
 
2150
        Если при целочисленных вычислениях одним из операндов будет переменная
2151
    типа float, то из него будет выделена целая часть, которая и примет
2152
    участие в вычислениях. При присваивании целочисленной переменной значения
2153
    переменной типа float, из нее также будет выделена целая часть, которая и
2154
    будет присвоена целочисленной переменной.
7544 leency 2155
Return to contents.
7496 leency 2156
 
2157
 
7544 leency 2158

7496 leency 2159
    6.7.6 Операции сравнения.
7544 leency 2160
7496 leency 2161
 
2162
        Если при операции сравнения левым операндом является переменная или
2163
    выражение типа float, а правым является целочисленное значение, то
2164
    целочисленное значение будет преобразовано в вещественный тип. Если же
2165
    левым операндом является целочисленное выражение или переменная, а правым
2166
    операндом значение типа float, то из правого операнда будет выделена целая
2167
    часть, которая и примет участие в сравнении.
7544 leency 2168
Return to contents.
7496 leency 2169
 
2170
 
7544 leency 2171

7496 leency 2172
    6.7.7 Сравнение переменных типа float с 32-битным регистром.
7544 leency 2173
7496 leency 2174
 
2175
        В регистрах могут содержаться знаковые, без знаковые и вещественные
2176
    данные. По умолчанию считается, что в регистре находится без знаковое целое
2177
    число. При сравнении переменных типа float с 32-битным регистром можно
2178
    указывать тип данных содержащихся в регистре.  Для этой цели можно
2179
    использовать модификаторы: signed, unsigned, float. Примеры:
2180
 
2181
    float f=1.0;
2182
 
2183
    void PROC()
2184
    {
7544 leency 2185
      IF( f < signed ECX)       //в регистре ECX находится знаковое число
7496 leency 2186
      IF( unsigned EBX > f) //в регистре EBX находится без знаковое число
2187
      IF( f == float EAX )  //в EAX находится число формата float
2188
    }
2189
 
2190
        ВНИМАНИЕ!  При операции сравнения с участием переменой типа float,
2191
    содержимое регистра AX будет разрушено.
7544 leency 2192
Return to contents.
7496 leency 2193
 
2194
 
7544 leency 2195

7496 leency 2196
  6.8 Указатели.
7544 leency 2197
7496 leency 2198
 
2199
      В C-- сейчас указатели реализованы не в полном объеме. Поэтому многие
2200
  вещи, которые возможны в обычных языках C, здесь будут недоступны.
2201
 
2202
      Пример применения указателей в C--:
2203
 
2204
  char *string[4]={"string1", "string2", "string3", 0}; //массив указателей
2205
  char *str="string4";
2206
 
2207
  main()
2208
  int i;
2209
  char *tstr;
2210
  {
7544 leency 2211
        FOR(i=0; string[i]!=0; i++){
2212
                WRITESTR(string[i]);
2213
                WRITELN();
2214
        }
2215
        FOR(tstr=str;byte *tstr!=0; tstr++){
2216
                WRITE(byte *tstr);
2217
        }
7496 leency 2218
  }
2219
 
2220
      Указатели можно использовать при передаче параметров процедурам, а в
2221
  самих процедурах в качестве как локальных, так и параметрических
2222
  переменных. Указатели можно также использовать в структурах. Можно
2223
  использовать указатели на указатели. Введена поддержка указателей на
2224
  процедуры:
2225
 
2226
  void (*proc)();  //объявление указателя на процедуру
2227
 
2228
      По умолчанию указатели на процедуру являются указателями на процедуру в
2229
  стиле pascal, независимо от регистра, в котором написано имя процедуры и
2230
  режима компиляции. Если Вам необходимо, чтобы был использован другой тип
2231
  вызова, то его необходимо указать при объявлении указателя на процедуру.
2232
 
2233
      При инициализации указателей компилятор не контролирует то, чем
2234
  инициализируется указатель. Т.е. Вы можете указателю на char присвоить
2235
  указатель на int или указателю на процедуру присвоить адрес переменной.
2236
  Это может вызвать ошибку в работе программы.
7544 leency 2237
Return to contents.
7496 leency 2238
 
2239
 
7544 leency 2240

7496 leency 2241
7. Адресация.
2242
 
2243
  7.1 Относительная адресация.
7544 leency 2244
7496 leency 2245
 
2246
      Изначально индексный доступ к элементам в массивах любого типа в
2247
  компиляторе осуществлялся побайтно, независимо от объявленного типа данных.
2248
  Индексы ограничены форматом поля RM процессора 8086, таким образом,
2249
  доступны только следующие форматы индексов (где индекс - значение
2250
  16-разрядной константы или постоянного выражения):
2251
 
2252
        variable[index]
2253
        variable[index+BX+SI]
2254
        variable[index+BX+DI]
2255
        variable[index+BP+SI]
2256
        variable[index+BP+DI]
2257
        variable[index+SI]
2258
        variable[index+DI]
2259
        variable[index+BP]
2260
        variable[index+BX]
2261
 
2262
      Начиная с версии 0.210, появилась возможность использовать в качестве
2263
  индекса переменных типа char byte int word long dword. При этом
2264
  доступ к элементам массива осуществляется в зависимости от объявленного типа
2265
  массива.
2266
 
2267
      Также начиная с версии 0.210 появилась возможность использовать в
2268
  качестве индексных и базовых регистров при относительной адресации любые
2269
  32-битные регистры.
2270
 
2271
      Если Вы для адресации к элементам массива будете использовать регистры и
2272
  числовые константы, из которых можно получить поле RM для инструкций 8086
2273
  процессора или комбинацию полей RM BASE и SIB для 80386 процессора, то
2274
  компилятор будет использовать эти регистры для генерации инструкции с этими
2275
  полями. В результате Вы получите относительную побайтную адресацию к
2276
  элементам массива.
2277
 
2278
      Если же из этих регистров невозможно получить поля RM, BASE, SIB,
2279
  или для адресации будет использована переменная, то компилятор сначала
2280
  вычислит это выражение в регистр (E)SI или другой, подходящий регистр, а
2281
  затем умножит содержимое этого регистра на разрядность Вашего массива. Таким
2282
  образом, в этом случае вы будете иметь поэлементную адресацию в массиве.
2283
  Пример:
2284
 
2285
    AX = var [ 5 ];
2286
    AX = var [ BX + 5 ];
2287
    AX = var [ BX + CX ];
2288
    AX = var [ i ];
2289
 
2290
      Компилятор сгенерирует следующий код:
2291
  test.c-- 7: AX=var[5];
2292
  0100 A12501                   mov     ax,[125h]
2293
 
2294
  test.c-- 8: AX=var[BX+5];
2295
  0103 8B872501                 mov     ax,[bx+125h]
2296
 
2297
  test.c-- 9: AX=var[BX+CX];
2298
  0107 89DE                     mov     si,bx
2299
  0109 01CE                     add     si,cx
2300
  010B 01F6                     add     si,si
2301
  010D 8B842001                 mov     ax,[si+120h]
2302
 
2303
  test.c-- 10: AX=var[i];
2304
  0111 8B362201                 mov     si,[122h]
2305
  0115 01F6                     add     si,si
2306
  0117 8B842001                 mov     ax,[si+120h]
2307
 
2308
      Как Вы видите, первые два выражения были преобразованы в одну
2309
  ассемблерную инструкцию, и получилась побайтная адресация. В двух следующих
2310
  выражениях получить одну ассемблерную инструкцию не удалось и компилятор
2311
  применил для этих выражений поэлементную адресацию.
2312
 
2313
      Такой двойственный подход реализован с целью сохранения совместимости
2314
  новых возможностей с предыдущими.
2315
 
2316
      Несмотря на кажущуюся для неискушенного пользователя путаницу, этот
2317
  механизм легко понять и запомнить по следующему простому правилу: если Вы
2318
  используете в качестве индекса только цифровое значение или регистр BX, SI,
2319
  DI, BP или любой 32-битный регистр, то компилятор сгенерирует код с
2320
  побайтной адресацией. Если же в качестве индекса будет использована
2321
  переменная, то компилятор сгенерирует код с поэлементной адресацией. Если
2322
  же Вы хорошо знакомы с ассемблером, то Вам не составит большого труда
2323
  понять в каких случаях Вы получите побайтную, а в каких поэлементную
2324
  адресацию.
2325
 
2326
      Иногда требуется иметь побайтный доступ к элементам массива используя в
2327
  качестве индекса переменную. Например
2328
 
2329
    AX=var[i];
2330
 
2331
      Для этого выражения будет сгенерирована поэлементная адресация, а нам
2332
  нужна побайтовая. Для этого можно написать так:
2333
 
2334
    SI=i;
2335
    AX=var[SI];
2336
 
2337
      Но можно это записать короче:
2338
 
2339
    AX=DSWORD[#var+i];
2340
 
2341
      В обоих этих случаях Вы получите побайтную адресацию к элементам массива
2342
  var. В первом варианте Вы сможете контролировать какой регистр будет
2343
  использован в качестве индекса, а во втором варианте компилятор будет сам
2344
  выбирать регистр для использования в качестве индекса.
2345
 
2346
      Важно всегда помнить о двойственном подходе компилятора к вычислению
2347
  адреса в массиве. Еще раз кратко:  если Вы в массиве адресуетесь используя
2348
  числовую константу или регистры BX,DI,SI,BP компилятор использует эти
2349
  значения без изменения.  Во всех других случаях будет коррекция значения в
2350
  зависимости от типа массива.
7544 leency 2351
Return to contents.
7496 leency 2352
 
2353
 
7544 leency 2354

7496 leency 2355
  7.2 Абсолютная адресация.
7544 leency 2356
7496 leency 2357
 
2358
      Абсолютная адресация также возможна.  Действуют те же самые ограничения
2359
  на индексы, что и при относительной адресации.
2360
 
2361
      Вычисленный индекс будет абсолютен в сегменте, регистр которого указан.
2362
  Можно указывать любой из регистров DS, CS, SS и ES. На процессорах 80386 и
2363
  более новых можно указывать также регистры FS и GS.
2364
 
2365
      Синтаксис - точно такой же, как и в относительной адресации, за
2366
  исключением того, что указывается не переменная, а сегмент и тип данных.
2367
  Могут применяться следующие указатели:
2368
 
2369
             // адресация в сегменте данных
2370
         DSBYTE  [смещение] // адресует байт в сегменте DS
2371
         DSWORD  [смещение] // адресует слово в сегменте DS
2372
         DSCHAR  [смещение] // адресует char в сегменте DS
2373
         DSINT   [смещение] // адресует int в сегменте DS
2374
         DSDWORD [смещение] // адресует dword в сегменте DS
2375
         DSLONG  [смещение] // адресует long в сегменте DS
2376
         DSFLOAT [смещение] // адресует float в сегменте DS
2377
 
2378
             // адресация в сегменте кода
2379
         CSBYTE  [смещение] // адресует байт в сегменте CS
2380
         CSWORD  [смещение] // адресует слово в сегменте CS
2381
         CSCHAR  [смещение] // адресует char в сегменте CS
2382
         CSINT   [смещение] // адресует int в сегменте CS
2383
         CSDWORD [смещение] // адресует dword в сегменте CS
2384
         CSLONG  [смещение] // адресует long в сегменте CS
2385
         CSFLOAT [смещение] // адресует float в сегменте CS
2386
 
2387
             // адресация в сегменте стека
2388
         SSBYTE  [смещение] // адресует байт в сегменте SS
2389
         SSWORD  [смещение] // адресует слово в сегменте SS
2390
         SSCHAR  [смещение] // адресует char в сегменте SS
2391
         SSINT   [смещение] // адресует int в сегменте SS
2392
         SSDWORD [смещение] // адресует dword в сегменте SS
2393
         SSLONG  [смещение] // адресует long в сегменте SS
2394
         SSFLOAT [смещение] // адресует float в сегменте SS
2395
 
2396
             // адресация в дополнительном сегменте данных
2397
         ESBYTE  [смещение] // адресует байт в сегменте ES
2398
         ESWORD  [смещение] // адресует слово в сегменте ES
2399
         ESCHAR  [смещение] // адресует char в сегменте ES
2400
         ESINT   [смещение] // адресует int в сегменте ES
2401
         ESDWORD [смещение] // адресует dword в сегменте ES
2402
         ESLONG  [смещение] // адресует long в сегменте ES
2403
         ESFLOAT [смещение] // адресует float в сегменте ES
2404
 
2405
             // адресация в дополнительном сегменте 2 (80386) +
2406
         FSBYTE  [смещение] // адресует байт в сегменте FS
2407
         FSWORD  [смещение] // адресует слово в сегменте FS
2408
         FSCHAR  [смещение] // адресует char в сегменте FS
2409
         FSINT   [смещение] // адресует int в сегменте FS
2410
         FSDWORD [смещение] // адресует dword в сегменте FS
2411
         FSLONG  [смещение] // адресует long в сегменте FS
2412
         FSFLOAT [смещение] // адресует float в сегменте FS
2413
 
2414
             // адресация в дополнительном сегменте 3 (80386) +
2415
         GSBYTE  [смещение] // адресуют байт в сегменте GS
2416
         GSWORD  [смещение] // адресуют слово в сегменте GS
2417
         GSCHAR  [смещение] // адресуют char в сегменте GS
2418
         GSINT   [смещение] // адресуют int в сегменте GS
2419
         GSDWORD [смещение] // адресуют dword в сегменте GS
2420
         GSLONG  [смещение] // адресуют long в сегменте GS
2421
         GSFLOAT [смещение] // адресует float в сегменте GS
2422
 
2423
  Примеры:
2424
     Загрузить в AL байт из ячейки с шестнадцатеричным адресом 0000:0417
2425
                 ES = 0x0000;
2426
                 AL = ESBYTE [0x417];
2427
 
2428
     Переместить слово из ячейки с шестнадцатеричным адресом 2233:4455
2429
     в ячейку с шестнадцатеричным адресом A000:0002
2430
                 $PUSH DS
2431
                 DS = 0x2233;
2432
                 ES = 0xA000;
2433
                 ESWORD [0x0002] = DSWORD [0x4455];
2434
                 $POP DS
2435
 
2436
     Сохранить вычисленное значение выражения X + 2, имеющее
2437
     тип int в ячейке с шестнадцатеричным адресом FFFF:1234
2438
                 ES = 0xFFFF;
2439
                 ESINT [0x1234] = X + 2;
2440
 
2441
     Сохранить BX в сегменте стека по смещению 42:
2442
                 SSWORD [42] = BX;
7544 leency 2443
Return to contents.
7496 leency 2444
 
2445
 
7544 leency 2446

7496 leency 2447
8. Работа с блоками данных.
2448
 
2449
  8.1 Структуры.
2450
 
2451
    8.1.1 Что такое структуры.
7544 leency 2452
7496 leency 2453
 
2454
        Структура позволяет объединить в одном объекте совокупность значений,
2455
    которые могут иметь различные типы.
7544 leency 2456
Return to contents.
7496 leency 2457
 
2458
 
7544 leency 2459

7496 leency 2460
    8.1.2 Синтаксис.
7544 leency 2461
7496 leency 2462
 
2463
    struct [<тег>] { <список-объявлений-элементов> }
2464
      <описатель>[,<описатель>...];
2465
    struct <тег> <описатель> [,<описатель>];
2466
 
2467
        Объявление структуры начинается с ключевого слова struct и имеет две
2468
    формы записи.
2469
 
2470
        В первой форме типы и имена элементов структуры специфицируются в
2471
    списке-объявлений-элементов. Необязательный в данном случае тег - это
2472
    идентификатор, который именует структурный тип, определенный данным
2473
    списком объявлений элементов. описатель специфицирует либо переменную
2474
    структурного типа, либо массив структур данного типа.
2475
 
2476
        Вторая синтаксическая форма объявления использует тег структуры для
2477
    ссылки на структурный тип, определенный где-то в другом месте программы.
2478
 
2479
        Список объявлений элементов представляет собой последовательность из
2480
    одной или более объявлений переменных. Каждая переменная, объявленная в
2481
    этом списке, называется элементом структуры.
2482
 
2483
        Элементы структуры запоминаются в памяти последовательно в том
2484
    порядке, в котором они объявляются. Выравнивание элементов внутри
2485
    структуры по умолчанию не производится. Но существует опция, включение
2486
    которой в командную строку позволяет иметь выравнивание и внутри
2487
    структуры. Сама структура выравнивается на четный адрес если включено
2488
    выравнивание.
2489
 
2490
      Примеры объявлений структур:
2491
 
2492
    struct test
2493
    {
2494
      int a;
2495
      char b[8];
2496
      long c;
2497
    } rr, ff[4];
2498
 
2499
        В этом примере объявлены структура с именем rr и массив из 4 структур
2500
    с именем ff. Всему набору переменных присвоено название (тег) test. Этот
2501
    тег можно использовать для объявления других структур. Например:
2502
 
2503
   struct test dd;
2504
 
2505
        Здесь объявлена структура с именем dd, имеющая набор элементов
2506
    описанных в теге test.
2507
 
2508
        При объявлении структур с ранее объявленным тегом ключевое слово
2509
   struct можно не писать. Т.е можно написать вот так:
2510
 
2511
      test dd;
7544 leency 2512
Return to contents.
7496 leency 2513
 
2514
 
7544 leency 2515

7496 leency 2516
    8.1.3 Инициализация структур при объявлении.
7544 leency 2517
7496 leency 2518
 
2519
        После объявления структуры ее элементы могут принимать произвольные
2520
    значения. Что бы этого не было надо структуры проинициализировать.
2521
    Инициализировать структуры при их объявлении можно только глобальные. C--
2522
    поддерживает несколько способов инициализации структур при их объявлении:
2523
 
2524
      1. Одним значением:
2525
 
2526
       struct test dd=2;
2527
 
2528
    В этом примере всем элементам структуры dd присваивается значение 2.
2529
 
2530
      2. Массивом значений:
2531
 
2532
       struct test dd={1,2,,6};
2533
 
2534
    В этом примере первому элементу структуры dd присваивается значение 1,
2535
    второму - 2, четвертому - 6. Пропущенным и не доинициализированным
2536
    значениям будет присвоено 0 значение.
2537
 
2538
      3. Командой FROM:
2539
 
2540
       struct test dd=FROM "file.dat";
2541
 
2542
    В этом примере на место где расположена структура dd при компиляции будет
2543
    загружено содержимое файла . Если размер файла больше чем размер
2544
    структуры, то лишние байты будут загружены в код программы, но они не
2545
    будут востребованы. Если размер файла меньше чем размер структуры, то
2546
    недостающие байты структуры будут заполнены нулями.
2547
 
2548
      4. Командой EXTRACT:
2549
 
2550
       struct test dd=EXTRACT "file.dat", 24, 10;
2551
 
2552
    В этом примере на место где расположена структура dd при компиляции будет
2553
    загружен фрагмент из файла file.dat длиной 10 байт со смещения 24.
2554
    Недостающие байты будут заполнены нулями.
7544 leency 2555
Return to contents.
7496 leency 2556
 
2557
 
7544 leency 2558

7496 leency 2559
    8.1.4 Инициализация структуры при выполнении программы.
7544 leency 2560
7496 leency 2561
 
2562
        При выполнении программы, кроме присвоения каждому элементу структуры
2563
    значения, можно проинициализировать всю структуру присвоением ей числа или
2564
    переменной. Примеры:
2565
 
2566
    void proc()
2567
    struct test aa[5],rr;
2568
    int i;
2569
    {
2570
      aa[0]=0x12345678;
2571
      aa[i]=int 0x12345678;
2572
      aa=long 0x12345678;
2573
      rr=i;
2574
 
2575
    В первом примере память, занимаемая первой структурой массива из 5
2576
    структур, будет заполнена байтом 0x78 (по умолчанию).
2577
 
2578
    Во втором примере память, занимаемая (i+1)-вой структурой массива из 5
2579
    структур, будет заполнена словом 0x5678.
2580
 
2581
    В третьем примере память, занимаемая всем массивом из 5 структур, будет
2582
    заполнена длинным словом 0x12345678.
2583
 
2584
    В четвертом примере память, занимаемая структурой rr, будет заполнена
2585
    содержимым переменной i.
2586
 
2587
        Можно также копировать содержимое одной структуры в другую. Например:
2588
 
2589
      rr=aa[2];
2590
 
2591
    Будет скопировано содержимое третьей структуры массива структур aa в
2592
    структуру rr.
7544 leency 2593
Return to contents.
7496 leency 2594
 
2595
 
7544 leency 2596

7496 leency 2597
    8.1.5 Операции с элементами структур.
7544 leency 2598
7496 leency 2599
 
2600
        С элементами структур можно выполнять все те операции, которые
2601
    доступны для переменных соответствующего типа. Например:  Объявлена
2602
    структура:
2603
 
2604
    struct test
2605
    {
2606
      int a;
2607
      char b[8];
2608
      long c;
2609
    } rr[3];
2610
    Пример допустимого синтаксиса:
2611
        rr.a = rr.b[i] * rr[1].c + i ;
2612
 
2613
    Примечание:
2614
        При операциях с элементами массива структур и с индексированными
2615
    элементами, в которых в качестве индекса или номера структуры используется
2616
    переменная, компилятор может использовать регистры SI и DI, а в некоторых
2617
    ситуациях (например:  rr[i].b[j] >< rr[i+1].b[j+2] ) будет задействован и
2618
    регистр DX.
2619
 
2620
        Для отдельных элементов структуры, можно получать их адрес, размер
2621
    и смещение в теге структуры. Вот пример:
2622
 
2623
    struct AA       //объявление тега структуры
2624
    {
2625
      word a[3];    // первый элемент структуры
2626
      char b;       // второй элемент структуры
2627
      long c;       // третий элемент структуры
2628
    };
2629
 
7544 leency 2630
    struct BB   //тег второй структуры
7496 leency 2631
    {
7544 leency 2632
      word aa;  // первый элемент
2633
      AA bb;    // второй элемент - вложенная структура
2634
    }ss;                // объявляем структуру с тегом BB
7496 leency 2635
 
2636
    void proc()
2637
    {
2638
      AX=#ss.bb.b; // получить адрес элемента b структуры bb в структуре ss
2639
      AX=#BB.bb.b; // получить смещение этого же элемента в теге BB
7544 leency 2640
      AX=sizeof(ss.bb); // получить размер элемента bb в структуре ss
2641
      AX=sizeof(BB.bb); // получить размер элемента bb в теге BB
7496 leency 2642
    }
7544 leency 2643
Return to contents.
7496 leency 2644
 
2645
 
7544 leency 2646

7496 leency 2647
    8.1.6 Вложенные структуры.
7544 leency 2648
7496 leency 2649
 
2650
        При объявлении тегов структур можно использовать теги других,
2651
    объявленных ранее структур. Пример вложенных структур:
2652
 
2653
    struct RGB
2654
    {
2655
      byte Red;
2656
      byte Green;
2657
      byte Blue;
2658
      byte Reserved;
2659
    };
2660
 
2661
    struct BMPINFO
2662
    {
2663
      struct BMPHEADER header; //описание этой структуры пропущено
2664
      struct RGB color[256];
2665
    }info;
2666
 
2667
        Предположим Вам нужно получить содержимое переменной Red десятого
2668
    элемента color. Это можно будет записать так:
2669
 
2670
      AL=info.color[10].Red;
2671
 
2672
        Но существует одно ограничение использования вложенных структур в C--.
2673
    Это невозможность использования переменной в качестве индекса более одного
2674
    раза при обращении к многоэкземплярным структурам. Поясним это на примере:
2675
 
2676
    struct ABC
2677
    {
2678
      int a;
2679
      int b;
2680
      int c;
2681
    };
2682
 
2683
    struct
2684
    {
2685
      struct ABC first[4];  //4 экземпляра структуры ABC
2686
      int d;
2687
    }second[4];
2688
 
2689
    int i,j;
2690
 
2691
    void proc()
2692
    {
2693
      AX=second[i].first[j].a; //такая запись вызовет сообщение об ошибка, так
2694
                               //как переменная использовалась в двух местах
2695
      AX=second[2].first[j].a; //а этот синтаксис допустим.
2696
      AX=second[i].first[3].a;
2697
    }
7544 leency 2698
Return to contents.
7496 leency 2699
 
2700
 
7544 leency 2701

7496 leency 2702
    8.1.7 Отображение тега структуры на блок памяти.
7544 leency 2703
7496 leency 2704
 
2705
        Отображение тега структуры на блок памяти является альтернативой
2706
    указателям на структуры.
2707
 
2708
        Альтернативный способ использования указателей на структуры позволит
2709
    Вам самим выбрать регистр, в котором будет хранится адрес структуры и
2710
    самим следить за его сохранностью и по мере необходимости восстанавливать
2711
    его содержимое.
2712
 
2713
        Объяснить, как использовать отображение тега структуры на память,
2714
    наверное, будет проще на примере:
2715
 
2716
    struct AA       //объявление тега структуры
2717
    {
2718
      word a[3];    // первый элемент структуры
2719
      char b;       // второй элемент структуры
2720
      long c;       // третий элемент структуры
2721
    };
2722
 
2723
    byte buf[256];  //блок памяти, на который будет отображен тег структуры
2724
 
2725
    void proc1()
2726
    {
2727
     ...
2728
     proc2 ( #buf );  // вызов процедуры с передачей ей в качестве параметра
2729
                      // адреса блока памяти
2730
     ...
2731
    }
2732
 
2733
    long proc2 (unsigned int pointer_to_mem)
2734
    {
2735
    int i;
2736
      BX=pointer_to_mem;  // в BX загрузим адрес блока памяти
2737
      FOR(i=0; i<3; i++){ // в массив элемента a записать -1
2738
        BX.AA.a[i]=-1;
2739
      }
2740
      BX.AA.b=0;
2741
      ES:BX.AA.c=EAX;
2742
      return BX.AA.c;  // вернуть содержимое элемента c
2743
    }
2744
 
2745
        В 16-битном режиме для хранения адреса структуры можно использовать
2746
    регистры: BX,DI,SI,BP. Но лучше для этого использовать регистр BX.
2747
    Регистры DI и SI может использовать компилятор при вычислении адреса
2748
    многоэлементных объектов. Регистр BP компилятор использует для работы с
2749
    локальными и параметрическими переменными. В 32-битном режиме можно
2750
    использовать любой кроме ESP и EBP регистр, а регистры EDI и ESI надо
2751
    использовать осторожно.
7544 leency 2752
Return to contents.
7496 leency 2753
 
2754
 
7544 leency 2755

7496 leency 2756
    8.1.8 Битовые поля структур.
7544 leency 2757
7496 leency 2758
 
2759
        Битовые поля структур используются для экономии памяти, поскольку
2760
    позволяют плотно упаковать значения, и для организации удобного доступа к
2761
    регистрам внешних устройств, в которых различные биты могут иметь
2762
    самостоятельное функциональное назначение.
2763
 
2764
        Объявление битового поля имеет следующий синтаксис:
2765
 
2766
    <тип> [<идентификатор>]:<константа>;
2767
 
2768
    или на примере:
2769
 
2770
    int var:5;  //объявление битового поля размером 5 бит с именем var
2771
 
2772
        Битовое поле состоит из некоторого числа битов, которое задается
2773
    числовым выражением константа. Его значение должно быть целым
2774
    положительным числом и его значение не должно превышать числа разрядов,
2775
    соответствующие типу определяемого битового поля. В C-- битовые поля
2776
    могут содержать только без знаковые значения. Нельзя использовать массивы
2777
    битовых полей, указатели на битовые поля.
2778
 
2779
        идентификатор именует битовое поле. Его наличие необязательно.
2780
    Неименованное битовое поле означает пропуск соответствующего числа битов
2781
    перед размещением следующего элемента структуры. Неименованное битовое
2782
    поле, для которого указан нулевой размер, имеет специальное назначение:
2783
    оно гарантирует, что память для следующего битового поля будет начинаться
2784
    на границе того типа, который задан для неименованного битового поля.
2785
    Т.е.  будет произведено выравнивание битового поля на 8/16/32 бита.
2786
 
2787
        В C-- все битовые поля упаковываются одно за другим независимо от
2788
    границ типа идентификаторов. Если последующее поле не является битовым
2789
    полем, то оставшиеся до границы байта биты не будут использованы.
2790
    Максимальный размер битового поля равен 32 бита для типа dword/long, 16
2791
    бит для типа word/int и 8 бит для типа byte/char. Битовые поля можно
2792
    объединять, т.е. использовать их в операторе union. sizeof
2793
    примененный к битовому полю вернет размер этого поля в битах. При
2794
    использовании битового поля, его содержимое будет расширятся в регистр
2795
    как без знаковое целое число.
7544 leency 2796
Return to contents.
7496 leency 2797
 
2798
 
7544 leency 2799

7496 leency 2800
  8.2 Объединения.
7544 leency 2801
7496 leency 2802
 
2803
      Объединения позволяют в разные моменты времени хранить в одном объекте
2804
  значения различного типа.
2805
 
2806
      Память, которая выделяется под объединение, определяется размером
2807
  наиболее длинного из элементов объединения. Все элементы объединения
2808
  размещаются в одной и той же области памяти с одного и того же адреса.
2809
  Значение текущего элемента объединения теряется, когда другому элементу
2810
  объединения присваивается значение.
2811
 
2812
      В C-- реализованы так называемые анонимные объединения. Т.е.
2813
  объединениям не присваивается имя, а обращение к элементам объединения
2814
  происходит как к обычной переменной. Пример:
2815
 
2816
  union
2817
  {
2818
    dword regEAX;
2819
    word  regAX;
2820
    byte  regAL;
2821
  };  // объявили, что 3 переменные расположены по одному и тому же
2822
      // физическому адресу
2823
 
2824
  void test()
2825
  {
7544 leency 2826
        regEAX = 0x2C;
2827
        BL = regAL;     //в регистре BL окажется значение 0x2C
7496 leency 2828
  }
2829
 
2830
      Объединять можно переменные различных типов, массивы, строковые
2831
  переменные и структуры. Объединения могут быть глобальными и локальными, а
2832
  также располагаться внутри структур (пока в объединениях внутри структур
2833
  нельзя использовать структуры). Глобальные объединения могут быть
2834
  инициализированными и неинициализированными. Чтобы получить
2835
  инициализированное объединение достаточно проинициализировать лишь первый
2836
  элемент объединения. Если же первый элемент объединения не инициализирован,
2837
  а следующие элементы инициализированы, то это вызовет сообщение компилятора
2838
  об ошибке.
7544 leency 2839
Return to contents.
7496 leency 2840
 
2841
 
7544 leency 2842

7496 leency 2843
  8.3 Команды 'FROM' и 'EXTRACT'.
7544 leency 2844
7496 leency 2845
 
2846
      В C-- есть очень оригинальные команды, которых нет в других языках. Это
2847
  FROM и EXTRACT.
2848
 
2849
      Команда FROM имеет синтаксис:
2850
 
2851
  <тип_переменной> <имя_переменной> = FROM <имя_файла>;
2852
 
2853
      Встретив эту команду при компиляции, компилятор загрузит в выходной
2854
  файл содержимое файла имя_файла, а имя_переменной будет идентификатором
2855
  начала загруженного кода. Вот пример использования этой команды из файла
2856
  tinydraw.c--:
2857
 
2858
  byte palette[PALSIZE] = FROM "TINYDRAW.PAL";  // buffer for palette
2859
 
2860
     Команда EXTRACT имеет синтаксис:
2861
 
2862
  <тип_переменной> <имя_переменной> = EXTRACT <имя_файла>, <начало>, <длина>;
2863
 
2864
      Встретив эту команду при компиляции, компилятор загрузит в выходной
2865
  файл из файла имя_файла число байт равное длина со смещения начало, а
2866
  имя_переменной будет идентификатором начала загруженного кода. Вот пример
2867
  использования этой команды:
2868
 
2869
  byte LIT128 = EXTRACT "8X16.FNT", 16*128, 16;
2870
  byte LIT130 = EXTRACT "8X16.FNT", 16*130, 16;
7544 leency 2871
Return to contents.
7496 leency 2872
 
2873
 
7544 leency 2874

7496 leency 2875
9. Операторы.
2876
 
2877
  9.1 Условные инструкции.
7544 leency 2878
7496 leency 2879
 
2880
      Условные инструкции, при помощи которых осуществляется ветвление, такие
2881
  же как в C.
2882
 
2883
      C-- имеет две инструкции ветвления. if и IF.
2884
 
2885
      if делает близкий условный переход, а IF делает короткий
2886
  (8-разрядный) условный переход. IF выполняется быстрее и может экономить
2887
  до 3 байт в размере кода, но может осуществлять переходы только в пределах
2888
  127 байтов кода.
2889
 
2890
      Условные инструкции, как и в C, могут сопровождаться, как одиночной
2891
  командой, так и блоком из нескольких команд, заключенных в фигурные скобки
2892
  { и }. Условные инструкции имеют те же ограничения, что и условные
2893
  выражения.
2894
 
2895
      Если за инструкцией IF следует больше чем 127 байтов кода, транслятор
2896
  выдаст следующее сообщение об ошибке:
2897
 
2898
          IF jump distance too far, use if.
2899
 
2900
  Это можно просто исправить, заменив в этом месте инструкцию IF на if.
2901
 
2902
      Команды else и ELSE используются точно так же, как в языке C.
2903
  Отличие их в том, что ELSE имеет ограничение адреса перехода 127 байт,
2904
  такое же как IF. else генерирует код на 1 байт длиннее, чем ELSE.
2905
 
2906
      Команды IF и else, а также if и ELSE могут свободно смешиваться
2907
  как в следующем примере:
2908
 
2909
          if( x == 2 )
2910
              WRITESTR("Two");
2911
          ELSE{ WRITESTR("not two.");
2912
                printmorestuff();
2913
              }
2914
 
2915
      Если за инструкцией ELSE следует больше чем 127 байтов кода,
2916
  транслятор выдаст следующее сообщение об ошибке:
2917
 
2918
          ELSE jump distance too far, use else.
2919
 
2920
      Это можно просто исправить, заменив в этом месте инструкцию ELSE на
2921
  else.
7544 leency 2922
Return to contents.
7496 leency 2923
 
2924
 
7544 leency 2925

7496 leency 2926
  9.2 Циклы do{} while.
7544 leency 2927
7496 leency 2928
 
2929
      В таком цикле блок кода, составляющий тело цикла, будет повторяться,
2930
  пока условное выражение имеет значение истинно.
2931
 
2932
      Истинность условного выражения проверяется после выполнения тела  цикла,
2933
  поэтому блок кода будет выполнен, по крайней мере, один раз.
2934
 
2935
   Пример do {} while цикла, в котором тело будет исполнено пять раз:
2936
 
2937
           count = 0;
2938
           do {
2939
              count++;
2940
              WRITEWORD(count);
2941
              WRITELN();
2942
              } while (count < 5);
2943
 
2944
  Условное выражение в do {} while инструкции должно соответствовать тем же
2945
  правилам, что и в инструкциях IF и if.
7544 leency 2946
Return to contents.
7496 leency 2947
 
2948
 
7544 leency 2949

7496 leency 2950
  9.3 Циклы loop, LOOPNZ, loopnz.
7544 leency 2951
7496 leency 2952
 
2953
      Циклы loop повторяют блок кода, пока определенная переменная или
2954
  регистр, выполняющие роль счетчика цикла, содержат значение, отличное от
2955
  нуля. В конце выполнения блока кода, составляющего тело цикла, указанная
2956
  переменная или регистр - уменьшается на 1, а затем проверяется на равенство
2957
  нулю. Если переменная (или регистр) не равна нулю, тело цикла будет
2958
  выполнено снова, и процесс повторится.
2959
 
2960
      Пример использования цикла loop в котором в качестве счетчика цикла
2961
  использована переменная:
2962
 
2963
          count = 5;
2964
          loop( count )
2965
              {WRITEWORD(count);
2966
              WRITELN();
2967
              }
2968
 
2969
      Наибольший эффект дает использование регистра CX для циклов с небольшим
2970
  телом, поскольку в этом случае компилятором генерируется цикл с применением
2971
  машинной команды LOOP.
2972
 
2973
      Если перед стартом счетчик циклов содержит нулевое значение, команды
2974
  тела цикла будут выполнены максимальное число раз для диапазона переменной
2975
  (256 раз для 8-битного счетчика (переменной типа byte или char), 65536 для
2976
  16-битного счетчика (переменной типа word или int), и 4294967296 для
2977
  32-битного счетчика (переменной типа dword или long).
2978
 
2979
   В следующем примере цикл будет выполнен 256 раз:
2980
 
2981
           BH = 0;
2982
           loop (BH)
2983
              {
2984
              }
2985
 
2986
      Если в команде не указано никакого счетчика цикла, цикл будет
2987
  продолжаться бесконечно.
2988
 
2989
      Следующий пример будет непрерывно выводить символ звездочки (*) на
2990
  экран:
2991
 
2992
          loop()
2993
              WRITE('*');
2994
 
2995
      Программист, если хочет, может использовать или изменять значение
2996
  переменной счетчика цикла внутри цикла.
2997
 
2998
      Например, следующий цикл выполнится только 3 раза:
2999
 
3000
           CX = 1000;
3001
           loop( CX )
3002
              {
3003
              IF( CX > 3 )
3004
                  CX = 3;
3005
              }
3006
 
3007
      Цикл можно также прервать оператором разрыва BREAK или break. Вот
3008
  тот же пример с использованием BREAK:
3009
 
3010
           CX = 1000;
3011
           loop( CX )
3012
              {
3013
              IF( CX > 3 )
3014
                  BREAK;
3015
              }
3016
 
3017
      Циклы LOOPNZ/loopnz отличаются от цикла loop, тем, что перед входом
3018
  в цикл проверяется равенство нулю аргумента цикла. Если аргумент равен
3019
  нулю, то тело цикла ни разу не выполнится (в цикле loop в этом случае
3020
  тело цикла выполнится максимальное число раз). Цикл LOOPNZ получается
3021
  максимально эффективным при оптимизации на размер кода, если в качестве
3022
  параметра-счетчика используется регистр CX/ECX. При этом компилятор
3023
  использует ассемблерные инструкции JCXZ/JECXZ и LOOP.
7544 leency 3024
Return to contents.
7496 leency 3025
 
3026
 
7544 leency 3027

7496 leency 3028
  9.4 Цикл while, WHILE.
7544 leency 3029
7496 leency 3030
 
3031
       Синтаксис:
3032
    while(<выражение>)
3033
         <оператор>
3034
 
3035
      Цикл выполняется до тех пор, пока значение выражения не станет
3036
  ложным. Вначале вычисляется выражение. Если выражение изначально ложно,
3037
  то тело оператора while вообще не выполняется и управление сразу
3038
  передается на следующий оператор программы.
3039
 
3040
      Цикл WHILE аналогичен циклу while, но при этом генерируется код на
3041
  3 байта короче. Размер сгенерированного кода в цикле WHILE должен быть
3042
  меньше 127 байт.
3043
 
3044
    Примеры:
7544 leency 3045
        while ( i < 20 ){
3046
                WRITEWORD(i);
3047
                i++;
3048
        }
7496 leency 3049
 
7544 leency 3050
        WHILE (i < 20 ) @WRITEWORD(i);  //цикл либо будет бесконечным либо не
7496 leency 3051
                                          //выполнится ни разу
7544 leency 3052
Return to contents.
7496 leency 3053
 
3054
 
7544 leency 3055

7496 leency 3056
  9.5 Цикл for, FOR.
7544 leency 3057
7496 leency 3058
 
3059
       Синтаксис:
3060
    for ([<начальное выражение>]; [<условие>]; [<приращение>])
3061
      <оператор>
3062
 
3063
      Цикл for выполняется до тех пор, пока значение условия не станет
3064
  ложным. Если условие изначально ложно, то тело оператора for вообще не
3065
  выполняется и управление сразу передается на следующий оператор программы.
3066
  Начальное выражение и приращение обычно используются для инициализации
3067
  и модификации параметров цикла.
3068
 
3069
      Первым шагом при выполнении for является вычисление начального
3070
  выражения, если оно имеется. Затем вычисляется условие и производится
3071
  его оценка следующим образом:
3072
 
3073
      1) Если условие истинно, то выполняется тело оператора. Затем
3074
  вычисляется приращение (если оно есть), и процесс повторяется.
3075
 
3076
      2) Если условие опущено, то его значение принимается за истину. В
3077
  этом случае цикл for представляет бесконечный цикл, который может
3078
  завершиться только при выполнении в его теле операторов break, goto,
3079
  return.
3080
 
3081
      3) Если условие ложно, то выполнение цикла for заканчивается и
3082
  управление передается следующему оператору.
3083
 
3084
      Цикл FOR аналогичен циклу for, но при этом генерируется код на 3
3085
  байта короче. Размер сгенерированного кода в цикле FOR должен быть меньше
3086
  127 байт.
3087
 
3088
    Примеры:
7544 leency 3089
        for(i=0;i<5;i++){
3090
                WRITESTR("СТРОКА ");
3091
                WRITEWORD(i);
3092
                WRITELN();
3093
        }
7496 leency 3094
 
3095
      Число начальных выражений и число приращений не ограничено. Каждый
3096
  оператор в начальных выражениях и приращениях должен разделяться
3097
  запятой. Пример:
3098
 
3099
        for ( a=1, b=2 ; a<5 ; a++, b+=a ) {...
3100
 
3101
      Также есть возможность логического объединения условий. Объединять
3102
  можно до 32 условий. Каждое объединяемое условие должно быть заключено в
3103
  скобки. Пример:
3104
 
3105
        for ( a=0 ; (a>=0) && (a<10) ; a++ ){...
7544 leency 3106
Return to contents.
7496 leency 3107
 
3108
 
7544 leency 3109

7496 leency 3110
  9.6 Оператор переключатель switch.
7544 leency 3111
7496 leency 3112
 
3113
      Синтаксис:
3114
    switch(<выражение>){
3115
      case <константа>:
3116
        <оператор>
7544 leency 3117
        ...
7496 leency 3118
      case <константа>:
3119
        <оператор>
7544 leency 3120
        ...
7496 leency 3121
      ...
3122
      default:
3123
        <оператор>
3124
    }
3125
      Оператор переключатель switch предназначен для выбора одного из
3126
  нескольких альтернативных путей выполнения программы. Выполнение начинается
3127
  с вычисления значения выражения. После этого управление передается одному
3128
  из операторов тела переключателя. В теле переключателя содержатся
3129
  конструкции: case константа:, которые синтаксически представляют собой
3130
  метки операторов. Оператор, получающий управление, - это тот оператор,
3131
  значение константы которого совпадают со значением выражения
3132
  переключателя. Значение константы должно быть уникальным.
3133
 
3134
      Выполнение тела оператора-переключателя switch начинается с выбранного
3135
  таким образом оператора, и продолжается до конца тела или до тех пор, пока
3136
  какой-либо оператор не передаст управление за пределы тела.
3137
 
3138
    Оператор, следующий за ключевым словом default, выполняется, если ни
3139
  одна из констант не равна значению выражения. Если default опущено, то
3140
  ни один оператор в теле переключателя не выполняется, и управление
3141
  передается на оператор, следующий за switch.
3142
 
3143
      Для выхода из тела переключателя обычно используется оператор разрыва
3144
  break (BREAK).
3145
 
3146
    Пример:
3147
    switch (i){
3148
      case 'A':
3149
        WRITE(i);
3150
        i++;
3151
        BREAK;
3152
      case 32:
3153
        WRITE('_');
3154
        i++;
3155
        BREAK;
3156
      default:
3157
        WRITE('i');
3158
    }
3159
 
3160
      Оператор switch сейчас в компиляторе может реализовываться трем
3161
  способами: двухтабличным, табличным и методом последовательных проверок.
3162
 
3163
      Табличный метод является самым быстрым, а при большом числе операторов
3164
  case и при незначительной разнице между максимальным и минимальным
3165
  значениями case он еще может быть и более компактным. Но у него есть и
3166
  недостатки: в 16-битном режиме компилятор всегда использует регистр BX, а в
3167
  32-битном режиме, если операндом switch является регистр, то его значение
3168
  будет разрушено.
3169
 
3170
      В методе последовательных проверок блок сравнений находится в начале
3171
  тела оператора switch, это позволяет избавиться от 1-2 лишних jmp. Но
3172
  компилятор не может определить, какой тип перехода использовать при
3173
  проверке значений case. Это будет Вашей заботой. Если размер кода от
3174
  начала тела оператора switch до места расположения оператора case
3175
  меньше 128 байт, можно использовать короткий переход. В этом случае Вы
3176
  можете указать оператор CASE, что приведет к генерации более компактного
3177
  кода. Компилятор в предупреждениях будет Вам подсказывать о возможности
3178
  использования операторов CASE. Использование оператора CASE в случаях,
3179
  когда размер блока кода более 128 байт приведет к выдаче компилятором
3180
  сообщения об ошибке.
3181
 
3182
     При двухтабличном методе создаются две таблицы - таблица адресов входа в
3183
  тело оператора switch/SWITCH и таблица значений case. Генерируется
3184
  процедура сравнения входного значения со значениями во второй таблице. Если
3185
  есть совпадение, то делается переход по адресу из второй таблицы. Этот
3186
  метод является самым медленным, но при большом числе значений case (более
3187
  15) он становится самым компактным.
3188
 
3189
      При оптимизации кода на размер, компилятор предварительно вычисляет
3190
  размер кода, который может быть получен всеми методами и реализует самый
3191
  компактный. При оптимизации на скорость преимущество отдается табличному
3192
  методу, если размер таблицы получается не слишком большим.
3193
 
3194
      Для оператора switch введена также и короткая его форма - SWITCH.
3195
  Ее можно применять в случае, если размер блока кода между началом тела
3196
  оператора и оператором default (если он отсутствует, то концом тела
3197
  оператора switch) меньше 128 байт. О возможности использования короткой
3198
  формы компилятор будет сообщать в предупреждениях.
3199
 
3200
      Для оператора case/CASE, который может использоваться только в теле
3201
  блока оператора switch/SWITCH, можно указывать диапазон значений. Сначала
3202
  надо указывать меньшее значение, затем после многоточия большее. Пример:
3203
 
3204
  switch(AX){
3205
    case 1...5:
3206
      WRITESTR("Range AX from 1 to 5");
3207
      BREAK;
3208
  };
3209
 
3210
      Раньше Вам бы пришлось писать более громоздкую конструкцию:
3211
 
3212
  switch(AX){
3213
    case 1:
3214
    case 2:
3215
    case 3:
3216
    case 4:
3217
    case 5:
3218
      WRITESTR("Range AX from 1 to 5");
3219
      BREAK;
3220
  };
3221
 
3222
      Кроме того, что новый формат записи более компактен и более читабелен,
3223
  но еще при этом компилятор создает более компактный и быстрый код.
7544 leency 3224
Return to contents.
7496 leency 3225
 
3226
 
7544 leency 3227

7496 leency 3228
  9.7 Оператор перехода goto, GOTO.
7544 leency 3229
7496 leency 3230
 
3231
       Синтаксис:
3232
       goto <метка>;
7544 leency 3233
        .
3234
        .
3235
        .
7496 leency 3236
  <метка>:
3237
 
3238
      Оператор перехода goto передает управление на оператор помеченный
3239
  меткой. Аналогом в ассемблере оператору goto является команда jmp near.
3240
  Аналогом в ассемблере оператору GOTO является команда jmp short.
7544 leency 3241
Return to contents.
7496 leency 3242
 
3243
 
7544 leency 3244

7496 leency 3245
  9.8 Оператор разрыва break, BREAK.
7544 leency 3246
7496 leency 3247
 
3248
      Оператор разрыва break прерывает выполнение операторов do-while,
3249
  for, switch, while, loop, loopnz, LOOPNZ. Он может содержаться
3250
  только в теле этих операторов. Управление передается оператору, следующему
3251
  за прерванным циклом.
3252
 
3253
      Оператор BREAK аналогичен break, но при этом генерируется код на 1
3254
  байт короче. Размер сгенерированного кода от места где применяется BREAK
3255
  до конца цикла должен быть меньше 127 байт.
3256
 
3257
    Примеры:
7544 leency 3258
        FOR (i=0; ; i++){
3259
                FOR(j=0; j < WIDTH; j++){
3260
                        IF(i==5)BREAK;
3261
                }
3262
                IF(i==10)BREAK;
3263
        }
3264
Return to contents.
7496 leency 3265
 
3266
 
7544 leency 3267

7496 leency 3268
  9.9 Оператор продолжения continue, CONTINUE.
7544 leency 3269
7496 leency 3270
 
3271
      Оператор продолжения continue передает управление на следующую
3272
  итерацию в циклах do-while, for, while, loop, loopnz. В циклах
3273
  do-while, while, loop следующая итерация начинается с вычисления
3274
  условного выражения. Для цикла for следующая итерация начинается с
3275
  вычисления выражения приращения, а затем происходит вычисление условного
3276
  выражения.
3277
 
3278
    Оператор CONTINUE аналогичен continue, но при этом генерируется код на
3279
  1 байт короче. Размер сгенерированного кода от места где применяется
3280
  CONTINUE до начала итерации должен быть меньше 127 байт.
7544 leency 3281
Return to contents.
7496 leency 3282
 
3283
 
7544 leency 3284

7496 leency 3285
  9.10 Логическое объединение условий.
7544 leency 3286
7496 leency 3287
 
3288
      Существует возможность логического объединения сравнений в условиях
3289
  IF и if, циклах do{}while, while{}, WHILE{}, for{} и FOR{}.
3290
  Каждое сравнение при их логическом объединении должно быть заключено в
3291
  скобки.  Объединять можно не более 32 сравнений.
3292
 
3293
      В отличие от C в C-- анализ логических объединений происходит слева
3294
  направо и все лишние скобки будут восприняты компилятором как ошибочные.
3295
  Это несколько снижает гибкость и возможности применения этих объединений,
3296
  но такова традиция и философия, заложенная в C--.
3297
 
3298
      Пример:
3299
 
7544 leency 3300
           if ( (a>3) && (b>4) || (c<8) ){
7496 leency 3301
 
3302
  Т.е. если произвести расшифровку этого условия, то получится следующее:
3303
  условие выполнится если a>3 и b>4 или a>3 и c<8.
7544 leency 3304
Return to contents.
7496 leency 3305
 
3306
 
7544 leency 3307

7496 leency 3308
  9.11 Переход через циклы.
7544 leency 3309
7496 leency 3310
 
3311
     Для операторов BREAK, break, CONTINUE, continue введена
3312
  поддержка числового параметра, определяющего, сколько циклов надо
3313
  пропустить, прежде чем будет выполнен этот оператор. Например, мы имеем три
3314
  вложенных цикла:
3315
 
3316
  do{
3317
     loop(CX){
3318
        for(BX=0;BX<10;BX++){
7544 leency 3319
         break;   //стандартный оператор
3320
         break 0; //break с параметром - пропустить 0 циклов
3321
         break 1; //break с параметром - пропустить 1 цикл
3322
         break 2; //break с параметром - пропустить 2 цикла
7496 leency 3323
        }
3324
  LABL0:
3325
     }
3326
  LABL1:
3327
  }while (DX!=0);
3328
  LABL2:
3329
 
3330
      В третьем цикле находится группа различных вариантов оператора break.
3331
  Первым стоит стандартный оператор break, при выполнении которого
3332
  управление будет передаваться за пределы третьего цикла - на метку LABL0.
3333
  Вторым идет оператор break 0, при выполнении которого будет пропущено 0
3334
  циклов и управление будет передано опять же на метку LABL0. Таким
3335
  образом, запись break и break 0 являются синонимами. Третьим идет
3336
  оператор break 1, при выполнении которого будет пропущен один цикл и
3337
  управление будет передано за пределы второго цикла на метку LABL1. Ну и
3338
  наконец, последним идет оператор break 2, при выполнении которого
3339
  компилятор пропустит два цикла и передаст управление за пределы третьего,
3340
  на метку LABL2. Метки в этом примере проставлены для удобства объяснения.
3341
  Ну и я надеюсь, Вам понятно, что значение параметра не может превышать
3342
  числа циклов находящихся перед текущим. Так для одиночного цикла этот
3343
  параметр может принимать максимальное и единственное значение - 0.
7544 leency 3344
Return to contents.
7496 leency 3345
 
3346
 
7544 leency 3347

7496 leency 3348
  9.12 Инвертирование флага проверки условий.
7544 leency 3349
7496 leency 3350
 
3351
      Инвертирование флага проверки условий в операциях сравнения if/IF
3352
  for/FOR while/WHILE происходит с помощью символа ! - not.
3353
 
3354
     Выражени
3355
 
3356
    IF ( NOTCARRYFLAG )...   и  IF ( ! CARRYFLAG )...
3357
    IF ( proc() == 0 )...    и IF ( ! proc() ) ...
3358
 
3359
  являются синонимами.
7544 leency 3360
Return to contents.
7496 leency 3361
 
3362
 
7544 leency 3363

7496 leency 3364
  9.13 Вычисление выражения, а затем проверка условия.
7544 leency 3365
7496 leency 3366
 
3367
      В операциях сравнения в левом операнде теперь допустимо использовать
3368
  вычисления выражения с присваиванием и операции инкремента, декремента.
3369
  Например:
3370
 
3371
    IF (i=a+2 != 0 )...
3372
    IF ( i++ )...
3373
    IF ( a-- )...
3374
    IF ( i+=4 == 0 )...
3375
 
3376
      Во всех этих примерах сначала произойдет вычисление выражения в левой
3377
  части операции сравнения, а потом будет произведено сравнение результата с
3378
  правой частью выражения сравнения.
7544 leency 3379
Return to contents.
7496 leency 3380
 
3381
 
7544 leency 3382

7496 leency 3383
  9.14 Проверка битов при операции сравнения.
7544 leency 3384
7496 leency 3385
 
3386
      Если в левой части выражения сравнения написано: BX & 5, то при
3387
  вычислении выражения содержимое регистра BX будет изменено инструкцией
3388
  and. Но иногда возникает необходимость в проверке битов без изменения
3389
  содержимого регистра BX. Для этих целей надо использовать инструкцию
3390
  test. Как же указать компилятору, в каких ситуациях использовать
3391
  инструкцию and, а в каких test? В стандартных языках C для этого
3392
  используется механизм приоритетов - если выражение заключено в скобки, то
3393
  производится его вычисление, если нет, то производится проверка. Но C-- не
3394
  поддерживает приоритетов. Для разрешения этой проблемы в C-- решено
3395
  использовать непосредственно саму инструкцию test. Вот допустимые
3396
  варианты синтаксиса:
3397
 
3398
  IF ( $test AX,5 )
3399
  IF ( ! $test AX,5)
3400
  IF ( asm test AX,5)
3401
  IF ( ! asm { test AX,5 } )
7544 leency 3402
Return to contents.
7496 leency 3403
 
3404
 
7544 leency 3405

7496 leency 3406
  9.15 Оператор перестановки.
7544 leency 3407
7496 leency 3408
 
3409
      В C-- есть оператор, который не встречается в других языках, это
3410
  оператор перестановки. Оператор перестановки меняет местами содержимое двух
3411
  переменных. Символьное обозначение этого оператора ><. Переменные с обеих
3412
  сторон оператора перестановки должны иметь одинаковый размер, 8 бит и 8
3413
  бит, 16 бит и 16 бит, или 32 бита и 32 бита.
3414
 
3415
    Вот некоторые примеры:
3416
 
3417
      AX >< BX; // сохраняет значение BX в AX и значение AX в BX
3418
      CH >< BL; // меняет местами содержимое регистров CH и BL
3419
      dog >< cat; /* меняет местами значения переменной dog и переменной cat*/
3420
      counter >< CX; // меняет местами значения переменной counter
3421
                     // и содержимое регистра CX
3422
 
3423
      Если перестановка осуществляется между двумя 8-разрядными переменными в
3424
  памяти, будет разрушено содержимое регистра AL. Если перестановка - между
3425
  двумя 16-разрядными переменными в памяти, будет разрушено содержимое
3426
  регистра AX. Если перестановка - между двумя 32-разрядными переменными в
3427
  памяти, будет разрушено содержимое EAX. В любом другом случае, например,
3428
  между переменной в памяти и регистром, значения всех регистров будут
3429
  сохранены.
7544 leency 3430
Return to contents.
7496 leency 3431
 
3432
 
7544 leency 3433

7496 leency 3434
  9.16 Оператор отрицания.
7544 leency 3435
7496 leency 3436
 
3437
      C-- поддерживает быстрый синтаксис смены знака переменной - оператор
3438
  отрицания. Поставив - (знак минус) перед идентификатором переменной памяти
3439
  или регистра и ; (точку с запятой) после идентификатора, вы смените знак
3440
  переменной памяти или регистра.
3441
 
3442
  Вот некоторые примеры:
3443
 
3444
           -AX; // результат тот же, что и при 'AX = -AX;' ,но быстрее.
3445
           -tree; // то же самое, что 'tree = -tree;' ,но быстрее.
3446
           -BH; // меняет знак BH.
7544 leency 3447
Return to contents.
7496 leency 3448
 
3449
 
7544 leency 3450

7496 leency 3451
  9.17 Оператор инверсии.
7544 leency 3452
7496 leency 3453
 
3454
      C-- поддерживает быстрый синтаксис выполнения логической инверсии
3455
  значения переменной - оператор инверсии. Поставив ! (восклицательный знак)
3456
  перед идентификатором переменной памяти или регистром и ; (точку с
3457
  запятой) после идентификатора, вы выполните логическую (выполнится
3458
  ассемблерная команда NOT) инверсию текущего значения переменной. Вот
3459
  некоторые примеры:
3460
 
3461
          !AX; // то же самое, что ' AX ^ = 0xFFFF; ' но быстрее.
3462
          !node; // заменяет значение 'node' его логической инверсией.
3463
          !CL; // то же самое, что ' CL ^ = 0xFF ' но быстрее.
7544 leency 3464
Return to contents.
7496 leency 3465
 
3466
 
7544 leency 3467

7496 leency 3468
  9.18 Специальные условные выражения.
7544 leency 3469
7496 leency 3470
 
3471
      C-- поддерживает восемь специальных условных выражений:
3472
 
3473
           CARRYFLAG
3474
           NOTCARRYFLAG
3475
           OVERFLOW
3476
           NOTOVERFLOW
3477
           ZEROFLAG
3478
           NOTZEROFLAG
3479
           MINUSFLAG
3480
           PLUSFLAG
3481
 
3482
      Они могут использоваться вместо любых нормальных условных выражений.
3483
  Если Вы желаете, например, выполнить блок кода только если установлен флаг
3484
  переноса, Вам следует использовать следующую последовательность команд:
3485
 
3486
           IF( CARRYFLAG )
3487
           {
3488
           // здесь вы чего-то делаете
3489
           }
3490
 
3491
      Если Вы желаете непрерывно выполнять блок кода до тех пор, пока не
3492
  установится флаг переполнения, Вам следует использовать нечто подобное
3493
  следующему куску кода:
3494
 
3495
          do {
3496
              // здесь вы опять чего-то делаете
3497
              } while( NOTOVERFLOW );
7544 leency 3498
Return to contents.
7496 leency 3499
 
3500
 
7544 leency 3501

7496 leency 3502
  9.19 Символ $ - вставляет текущий адрес программы.
7544 leency 3503
7496 leency 3504
 
3505
      Символ $, кроме того, что является признаком последующей ассемблерной
3506
  инструкции, в языке C--, как и в языке Assembler может указывать текущий
3507
  адрес (смещение) компилируемой программы. Но в C-- он имел ограниченные
3508
  возможности. Он мог быть использован лишь как аргумент в операторах
3509
  GOTO/goto и ассемблерных инструкциях DW/DD/JMP.
3510
 
3511
      Этот символ может находиться в любом месте вычисляемого числового
3512
  выражения и может быть применен в любом месте совместно с другими числовыми
3513
  выражениями.
3514
 
3515
  Примеры применения:
3516
 
7544 leency 3517
  DW #main-$    //записать расстояние от процедуры main до текущего места
3518
  GOTO $+2;     //перейти по адресу на 2 больше, чем текущий адрес
3519
Return to contents.
7496 leency 3520
 
3521
 
7544 leency 3522

7496 leency 3523
  9.20 Ключевое слово static и оператор ::.
7544 leency 3524
7496 leency 3525
 
3526
      Если перед объявлением глобальной переменной, структуры или процедуры
3527
  указать слово static, то эти переменная, структура или процедура будут
3528
  доступны только в том файле, в котором они были объявлены. Т.е. если Вы
3529
  включите этот файл в другой директивой include, то переменные объявленные
3530
  во включаемом файле со словом static не будут доступны в основном файле,
3531
  и Вы можете в основном файле объявить другие переменные с такими же
3532
  именами.
3533
 
3534
      Если Вы примените слово static при объявлении локальной переменной в
3535
  процедуре, то память для этой переменной будет выделена не в стеке, а в
3536
  области данных процедуры. Но эта переменная будет доступна только внутри
3537
  процедуры, в которой она была объявлена. Применение static к локальным
3538
  переменным дает возможность сохранять значение переменной для следующего
3539
  входа в процедуру.
3540
 
3541
      Слово static можно применять к любому глобальному объекту
3542
  (переменной, структуре, процедуре). Для локального использования это слово
3543
  можно применять только к переменным.
3544
 
3545
      Если в Вашей программе есть глобальная и локальная переменная с
3546
  одинаковыми именами, то в процедуре, в которой объявлена эта локальная
3547
  переменная, Вы не имели доступа к одноименной глобальной переменной.
3548
  Применив перед именем переменной оператор ::, Вы получите доступ к
3549
  глобальной переменной.  Пример:
3550
 
3551
  int var;  //объявляем глобальную переменную
3552
 
3553
  void proc()
3554
  int var;  //объявляем локальную переменную с именем уже существующей
3555
            //глобальной переменной
3556
  {
7544 leency 3557
    (E)AX=var;  //имеем доступ только к локальной переменной
7496 leency 3558
    (E)AX=::var;  //а так можно получить доступ к глобальной переменной
3559
  }
7544 leency 3560
Return to contents.
7496 leency 3561
 
3562
 
7544 leency 3563

7496 leency 3564
  9.21 Оператор sizeof.
7544 leency 3565
7496 leency 3566
 
3567
      Операция sizeof определяет размер памяти, который соответствует объекту
3568
  или типу. Операция sizeof имеет следующий вид:
3569
 
3570
   sizeof (<имя типа>)
3571
 
3572
      Результатом операции sizeof является размер памяти в байтах,
3573
  соответствующий заданному объекту или типу.
3574
 
3575
      В C-- оператор sizeof можно применять к переменным, регистрам, типам
3576
  переменных, структурам, процедурам, текстовым строкам и файлам.
3577
 
3578
      Если операция sizeof применяется к типу структуры, то результатом
3579
  является размер тега данной структуры.
3580
 
3581
      Если операция sizeof применяется к текстовой строке, то результатом
3582
  операции является размер строки плюс завершающий нуль. Например:
3583
 
3584
   sizeof ("Test")
3585
 
3586
  результатом этой операции будет число 5. Если Вы напишите такую
3587
  конструкцию:
3588
 
3589
  char a="Test";
3590
 
3591
   sizeof(a)
3592
 
3593
  то результатом будет 5 - размер памяти, отведенный для переменной a.
3594
 
3595
      При использовании оператора sizeof с именем структуры вставляет
3596
  фактический размер памяти, занимаемый структурой. Это особенно важно, если
3597
  Вы объявили массив структур.
3598
 
3599
      Оператор sizeof можно применять и к имени определенной ранее
3600
  процедуры. Результатом будет размер этой процедуры. Но для динамических
3601
  процедур всегда будет ноль.
3602
 
3603
      Операцию sizeof можно применять и к файлам. Это бывает очень полезным
3604
  при использовании оператора FROM, но может применяться и в других случаях.
3605
  Пример применения оператора sizeof к файлам:
3606
 
3607
   sizeof ( file "filename.dat" )
3608
 
3609
  Результатом этой операции будет размер файла "filename.dat".
7544 leency 3610
Return to contents.
7496 leency 3611
 
3612
 
7544 leency 3613

7496 leency 3614
  9.22 Метки перехода.
7544 leency 3615
7496 leency 3616
 
3617
      Метки перехода применяются для указания начальных точек участков кода,
3618
  используемых командами перехода встроенного ассемблера и операторами
3619
  goto/GOTO.
3620
 
3621
      Имеются два типа меток перехода: глобальные и локальные. Глобальные
3622
  метки, как следует из названия, это метки, которые видимы из любого места в
3623
  программе. Локальные метки видны только в пределах своего процедурного
3624
  блока, и не определены за его пределами.
3625
 
3626
      Метки определяются идентификатором, оканчивающимися двоеточием. Если
3627
  идентификатор содержит хотя бы один символ строчных букв (букв нижнего
3628
  регистра, маленьких букв), это глобальная метка перехода, в противном
3629
  случае, это локальная метка перехода.
3630
 
3631
      Глобальные метки перехода не должны использоваться внутри динамических
3632
  процедур; там можно использовать только локальные метки. Это важно помнить,
3633
  поскольку, из-за применения такого средства как макрокоманды, динамическая
3634
  процедура может присутствовать в нескольких местах кода, что будет
3635
  означать, что метке соответствует больше чем один адрес.
3636
 
3637
      Метки вне процедур фактически располагаются в области данных программы.
3638
  Если данные и код находятся в одном сегменте (а именно так организованна
3639
  программа, написанная на C--), то метки вне процедур становятся простым и
3640
  эффективным методом для получения расстояний между частями программы. В
3641
  качестве имен для меток вне процедур могут быть использованы уникальные
3642
  идентификаторы, в которых можно использовать большие, маленькие и смесь
3643
  больших и маленьких букв.
7544 leency 3644
Return to contents.
7496 leency 3645
 
3646
 
7544 leency 3647

7496 leency 3648
10. Ассемблер.
3649
 
3650
  10.1 Поддержка команд ассемблера.
7544 leency 3651
7496 leency 3652
 
3653
      Встроенный в C-- ассемблер поддерживает все инструкции 8088/8086,
3654
  80286, 80386, 80486, Pentium, Pentium II и Pentium III процессоров.
3655
 
3656
      Все инструкции встроенного ассемблера должны начинаться с символа
3657
  доллара $. Поддерживается также ключевое слово asm, которое являясь
3658
  синонимом к символу доллара, еще и поддерживает объединение ассемблерных
3659
  инструкций в блоки.
7544 leency 3660
Return to contents.
7496 leency 3661
 
3662
 
7544 leency 3663

7496 leency 3664
  10.2 Ключевое слово asm.
7544 leency 3665
7496 leency 3666
 
3667
      Ключевое слово asm является синонимом к $ - префикс ассемблерной
3668
  команды. После слова asm можно писать блок ассемблерных команд.  Пример:
3669
 
7544 leency 3670
        asm {
3671
                .
3672
                .
3673
                push AX
7496 leency 3674
  labl:
7544 leency 3675
                push BX
3676
                mov AX,0x1234
3677
                jmp short labl
3678
                .
3679
                .
3680
                .
3681
        }
7496 leency 3682
 
3683
    Метки внутри блока ассемблерных команд допустимы.
7544 leency 3684
Return to contents.
7496 leency 3685
 
3686
 
7544 leency 3687

7496 leency 3688
  10.3 Префикс dup - повторение инструкций DB/DW/DD.
7544 leency 3689
7496 leency 3690
 
3691
      Для ассемблерных инструкции DB, DW, DD введена возможность использовать
3692
  префикс повторений dup. Применение этого префикса имеет следующий
3693
  синтаксис:
3694
 
3695
    $DW NUMREP dup VALTOREP
3696
 
3697
  NUMREP - число повторов инструкции DW.
3698
  VALTOREP - величина, которая будет повторена NUMREP раз.
3699
 
3700
      В отличие от аналога этого префикса из ассемблера повторяемую величину
3701
  заключать в скобки нельзя.
7544 leency 3702
Return to contents.
7496 leency 3703
 
3704
 
7544 leency 3705

7496 leency 3706
  10.4 Инструкции процессора Pentium III.
7544 leency 3707
7496 leency 3708
 
3709
      В компилятор добавлена поддержка 19 новых инструкций MMX расширения
3710
 
3711
  MASKMOVQ   mmx,mmx
3712
  MOVNTQ     m64,mmx
3713
  PAVGB      mmx,mmx/m64
3714
  PAVGW      mmx,mmx/m64
3715
  PEXTRW     r32,mmx,i8
3716
  PINSRW     mmx,r32/m16,i8
3717
  PMAXUB     mmx,mmx/m64
3718
  PMAXSW     mmx,mmx/m64
3719
  PMINUB     mmx,mmx/m64
3720
  PMINSW     mmx,mmx/m64
3721
  PMOVMSKB   r32,mmx
3722
  PMULHUW    mmx,mmx/m64
3723
  PREFETCHT0 mem
3724
  PREFETCHT1 mem
3725
  PREFETCHT2 mem
3726
  PREFETCHNTA mem
3727
  SFENCE
3728
  PSADBW     mmx,mmx/m64
3729
  PSHUFW     mmx,mmx/m64,i8
3730
 
3731
      и 46 инструкций SSE расширения.
3732
 
3733
  ADDPS      xmm,m128/xmm
3734
  ADDSS      xmm,xmm/m32
3735
  ANDNPS     xmm,xmm/m128
3736
  ANDPS      xmm,xmm/m128
3737
  COMISS     xmm,xmm/m32
3738
  DIVPS      xmm,m128/xmm
3739
  DIVSS      xmm,xmm/m32
3740
  MAXPS      xmm,m128/xmm
3741
  MAXSS      xmm,xmm/m32
3742
  MINPS      xmm,m128/xmm
3743
  MINSS      xmm,xmm/m32
3744
  MULPS      xmm,m128/xmm
3745
  MULSS      xmm,xmm/m32
3746
  ORPS       xmm,xmm/m128
3747
  RCPPS      xmm,xmm/m128
3748
  RCPSS      xmm,xmm/m32
3749
  RSQRTPS    xmm,xmm/m128
3750
  RSQRTSS    xmm,xmm/m32
3751
  SQRTPS     xmm,m128/xmm
3752
  SQRTSS     xmm,xmm/m32
3753
  SUBPS      xmm,m128/xmm
3754
  SUBSS      xmm,xmm/m32
3755
  UCOMISS    xmm,xmm/m32
3756
  UNPCKHPS   xmm,xmm/m128
3757
  UNPCKLPS   xmm,xmm/m128
3758
  XORPS      xmm,xmm/m128
3759
  CMPPS      xmm,xmm/m128,i8
3760
  CMPSS      xmm,xmm/m32,i8
3761
  SHUFPS     xmm,xmm/m128,i8
3762
  CVTPI2PS   xmm,m64/mmx
3763
  CVTSI2SS   xmm,m32/r32
3764
  CVTPS2PI   mmx,m128/xmm
3765
  CVTTPS2PI  mmx,xmm/m128
3766
  CVTSS2SI   r32,xmm/m128
3767
  CVTTSS2SI  r32,xmm/m128
3768
  LDMXCSR    m32
3769
  STMXCSR    m32
3770
  MOVHLPS    xmm,xmm
3771
  MOVLHPS    xmm,xmm
3772
  MOVMSKPS   r32,xmm
3773
  MOVNTPS    m128,xmm
3774
  MOVAPS     m128/xmm,xmm/m128
3775
  MOVSS      xmm/m32,xmm/m32
3776
  MOVUPS     xmm/m128,m128/xmm
3777
  MOVHPS     xmm/m64,m64/xmm
3778
  MOVLPS     xmm/m64,m64/xmm
3779
 
3780
      Многие из этих инструкций могут использовать в качестве операнда
3781
  64-битные и 128-битные ячейки памяти. Компилятор C-- сейчас может работать
3782
  только с 32-битными переменными. Поэтому для инструкций использующих в
3783
  качестве операнда ячейки памяти размером больше 32-бит можно использовать
3784
  переменные любых типов. Компилятор не будет выдавать на это сообщений об
3785
  ошибке, будет использован адрес этой переменной, а сама инструкция будет
3786
  использовать нужное ей число битов памяти, начиная с адреса указанной
3787
  переменной. Например:
3788
 
3789
      Для инструкции movaps один из операндов может быть 128-битной
3790
  ячейкой памяти. Для этой инструкции допустимы следующий синтаксис:
3791
 
3792
  byte  var8_128[16];
3793
  word  var16_128[8];
3794
  dword var32_128[4];
3795
 
3796
  void proc()
3797
  {
3798
  asm{
3799
    movaps var8_128,xmm0 //в массив из 16 байт будет записано содержимое XMM0
7544 leency 3800
    movaps xmm1,var16_128       //в XMM1 будет записано содержимое 8 слов
7496 leency 3801
    movaps var32_128,xmm1 //в массив из 4 двойных слов будет записано XMM1
3802
  }
3803
  }
7544 leency 3804
Return to contents.
7496 leency 3805
 
3806
 
7544 leency 3807

7496 leency 3808
11. Процедуры.
3809
 
3810
  11.1 Типы процедур, функций и макрокоманд.
7544 leency 3811
7496 leency 3812
 
3813
      Сейчас C-- поддерживает 4 типа вызова процедур: cdecl, pascal, stdcall
3814
  и fastcall. Вот краткие характеристики этих типов вызовов процедур:
3815
 
3816
  cdecl  Этот тип вызова процедур является по умолчанию для языка С. Он
3817
  характеризуется тем, что параметры процедуры передаются в порядке обратном
3818
  их записи. Очистка стека от параметров производится после завершения работы
3819
  процедуры. Этот способ вызова процедур очень удобен для процедур с
3820
  переменным числом параметров.
3821
 
3822
  pascal  Этот тип вызова предполагает, что параметры передаются в том
3823
  порядке, в котором они записаны в программе. Освобождение стека от
3824
  параметров производит сама вызываемая процедура. Этот тип вызова является
3825
  более компактным, чем cdecl.
3826
 
3827
  stdcall  Этот тип вызова является гибридом первых двух. Параметры
3828
  передаются процедуре в порядке обратном, тому в котором они записаны в
3829
  программе. Освобождение стека от параметров производится в самой вызываемой
3830
  процедуре.
3831
 
3832
  fastcall  Этот тип вызова процедур предполагает что передача параметров
3833
  процедуре производится через регистры, тем самым отпадает необходимость
3834
  освобождения стека от параметров. Для этого типа вызова процедуры
3835
  существуют ограничения по числу передаваемых параметров. Для C это три
3836
  параметра, а для C-- шесть. В C-- параметры передаются по умолчанию в
3837
  следующем порядке: 1-й - AX/EAX, 2-й - BX/EBX, 3 - CX/ECX, 4 - DX/EDX, 5 -
3838
  DI/EDI, 6 - SI/ESI. Параметры типов char или byte могут передаваться в
3839
  количестве не более 4 или только в первых 4 регистрах: 1 - AL, 2 - BL, 3 -
3840
  CL, 4 - DL. Этот порядок регистров может быть изменен, если явно указать
3841
  его либо при объявлении процедуры, либо при ее определении. Процедуры типа
3842
  fastcall иногда еще называют регистровыми.
3843
 
3844
      В C-- по умолчанию, если имя процедуры написано большими буквами, то
3845
  считается, что эта процедура имеет тип вызова fastcall. Если же в имени
3846
  процедуры есть хотя бы одна маленькая буква, то по умолчанию считается, что
3847
  эта процедура имеет тип вызова pascal, за исключением программ
3848
  компилируемых с ключом /w32 /w32c или /DLL. В них по умолчанию применяется
3849
  тип вызова процедур stdcall. Если же Вы хотите изменить тип вызова процедур
3850
  из по умолчанию на любой другой, то эту процедуру надо обязательно объявить
3851
  с указанием типа желаемого вызова.
3852
 
3853
      Объявление процедур введено для того, чтобы сообщать компилятору о
3854
  типе возврата из процедур, способе передачи параметров процедуре и их числе.
7544 leency 3855
Return to contents.
7496 leency 3856
 
3857
 
7544 leency 3858

7496 leency 3859
  11.2 Стековые процедуры.
7544 leency 3860
7496 leency 3861
 
3862
      Стековые процедуры по умолчанию объявляются при помощи идентификатора,
3863
  который содержит, по крайней мере, один символ строчных букв (букв нижнего
3864
  регистра, маленьких букв). Таким образом, стековые процедуры легко отличимы
3865
  от регистровых процедур, поскольку для имен регистровых процедур символы
3866
  строчных букв запрещены.
3867
 
3868
      Параметры для стековых процедур, если они есть, могут иметь любой тип
3869
  byte, char, word, int, dword, long или float.
3870
 
3871
      Параметры передаются в соответствии с правилами, принятыми для данного
3872
  типа процедур. Если процедура не имеет объявления, то компилятор не следит
3873
  за числом и типом передаваемых параметров. В этом случае у Вас появляется
3874
  свобода в их использовании, но Вы должны осознавать и последстви
3875
  неправильного их использования.
3876
 
3877
      В списке параметров для каждого параметра указывается его тип.
3878
  Параметры одного типа, идущие подряд, разделяются запятыми. Формальные
3879
  параметры разного типа в объявлении функции разделяются символом ;.
3880
 
3881
      В следующем примере стековая процедура возвращает сумму всех своих
3882
  параметров (имеющих различные типы) как величину типа word:
3883
 
7544 leency 3884
        word add_them_all (int a,b,c; byte d,e; word x,y)
3885
        {
3886
        return( a+b+c+d+e+x+y );
3887
        }
7496 leency 3888
 
3889
      Ранее C-- делал вызовы стековых процедур лишь в стиле pascal.
3890
  Преимуществом этого способа вызова процедур является компактность и более
3891
  простой механизм генерации кода. К недостаткам, а соответственно и
3892
  преимуществам С-стиля, можно отнести жесткую привязанность паскалевских
3893
  процедур к числу и типу передаваемых параметров (попробуйте при вызове
3894
  процедуры в стиле pascal опустить один параметр и получите 100% зависание).
3895
  Напомню некоторые технические детали обоих типов вызовов процедур.
3896
 
3897
  Кадр стека C-- для близких процедур стека в стиле pascal:
3898
       АДРЕС
3899
        ...
3900
      BP + FFFE предпоследний байта локальных переменных
3901
      BP + FFFF последний байт локальных переменных
3902
      BP + 0000 Сохраненный BP
3903
      BP + 0002 RET адрес
3904
      BP + 0004 последнее слово передаваемых процедуре параметров (если они
3905
                есть)
3906
      BP + 0006 предпоследнее слово передаваемых процедуре параметров
3907
       ...
3908
      BP + nnnn первое слово передаваемых процедуре параметров
3909
 
3910
      Освобождение стека от переданных процедуре параметров происходит прямо
3911
  в самой процедуре командой RET nnnn - где nnnn является размером переданных
3912
  в стек параметров.
3913
 
3914
  Кадр стека C-- для близких процедур стека в стиле си:
3915
       АДРЕС
3916
        ...
3917
      BP + FFFE предпоследний байта локальных переменных
3918
      BP + FFFF последний байт локальных переменных
3919
      BP + 0000 Сохраненный BP
3920
      BP + 0002 RET адрес
3921
      BP + 0004 первое слово передаваемых процедуре параметров (если они
3922
                есть)
3923
      BP + 0006 второе слово передаваемых процедуре параметров
3924
       ...
3925
      BP + nnnn последнее слово передаваемых процедуре параметров
3926
 
3927
      Процедуры в стиле С заканчиваются командой RET. Освобождение стека от
3928
  параметров происходит в том месте откуда была вызвана процедура. Обычно это
3929
  делается командой ADD SP,nnnn. Т.е. компилятор может точно знать сколько и
3930
  каких параметров Вы передаете в данном случае процедуре и соответственно
3931
  освобождает стек после завершения процедуры. Это очень удобно для процедур,
3932
  которые могут обрабатывать переменное число параметров (например, процедуры
3933
  типа printf).
3934
 
3935
      Объявление процедуры имеет следующий вид:
3936
 
3937
    rettype modif procname();
3938
 
3939
      Первым идет необязательный тип возврата из процедур. По умолчанию он
3940
  для 16-битных программ равен word, а для 32-битных dword. Затем должен идти
3941
  также необязательный модификатор. По умолчанию все стековые процедуры в C--
3942
  (за исключением режима компиляции программ под Windows, где по умолчанию
3943
  действует стиль вызова процедур stdcall) имеют стиль pascal. Далее идет им
3944
  процедуры со скобками, которые являются признаком того что Вы объявляете
3945
  процедуру, а не переменную. Завершает объявление символ точка с запятой.
3946
 
3947
      При объявлении процедур в C-- прописывать параметры процедуры
3948
  необязательно (тогда компилятор не будет контролировать число и тип
3949
  передаваемых параметров), но если Вы их вставите, то включится механизм
3950
  контроля за числом и типом параметров.
7544 leency 3951
Return to contents.
7496 leency 3952
 
3953
 
7544 leency 3954

7496 leency 3955
  11.3 Регистровые процедуры.
7544 leency 3956
7496 leency 3957
 
3958
      Регистровые процедуры определяются, по умолчанию, при помощи
3959
  идентификатора, который не содержит символов строчных букв. Или же явным
3960
  указанием что это регистровая процедура с помощью ключевого слова fastcall.
3961
 
3962
      Как уже было сказано, параметры (если они есть) для регистровой
3963
  процедуры передаются через регистры. Регистровые процедуры могут иметь не
3964
  более 6 параметров. Если параметры имеют тип int или word, регистры по
3965
  умолчанию используются в следующем порядке: AX, BX, CX, DX, DI, и SI.
3966
  Первые четыре параметра могут также иметь тип char или byte, в этом случае
3967
  задействуются регистры AL, BL, CL и DL соответственно. Любой из шести
3968
  параметров может иметь тип long, dword или float, тогда для него
3969
  используется регистр EAX, EBX, ECX, EDX, EDI, или ESI.
3970
 
3971
      В следующем примере регистровая процедура с именем TOGETHER возвращает
3972
  значение типа word как результат умножения первого параметра, имеющего тип
3973
  word, на второй параметр того же типа:
3974
 
3975
           word TOGETHER() /* AX = первый параметр, BX = второй параметр */
3976
           {
3977
           return (AX * BX);
3978
           }
3979
 
3980
      В следующем примере регистровая процедура с именем SHOW_NUM, которая не
3981
  возвращает никакого значения, зато выводит на экран первый параметр
3982
  (имеющий тип int), затем разделительный знак в виде двоеточия :, а затем
3983
  второй параметр (имеющий тип byte) :
3984
 
3985
           void SHOW_NUM () /* AX = первое число, BL = второе число */
3986
           {
3987
           $ PUSH BX
3988
           WRITEINT (int AX);
3989
           WRITE (':');
3990
           $ POP BX
3991
           WRITEWORD (BL);
3992
           }
3993
 
3994
      Но если в процедуре сделать объявление порядка и типов используемых
3995
  регистров, то возможно произвольное использование регистров. Более подробно
3996
  об этом можно почитать в разделе об объявлениях параметров в регистровых
3997
  процедурах.
3998
 
3999
      Для того, чтобы использовать регистровую процедуру как макрокоманду,
4000
  она должна быть объявлена как динамическая процедура. Динамические
4001
  процедуры описаны в следующем подразделе.
7544 leency 4002
Return to contents.
7496 leency 4003
 
4004
 
7544 leency 4005

7496 leency 4006
  11.4 Динамические процедуры.
7544 leency 4007
7496 leency 4008
 
4009
      Динамические процедуры - процедуры, которые определены, но вставляются
4010
  в код программы, только если есть вызов. Динамические процедуры могут
4011
  использоваться как макрокоманды.
4012
 
4013
      Определение динамической процедуры начинается с символа двоеточия ':'.
4014
 
4015
  Пример динамической процедуры стека:
4016
 
4017
          : void setvideomode (byte mode)
4018
          {
4019
          AL = mode;
4020
          AH = 0;
4021
          $ INT 0x10
4022
          }
4023
 
4024
  Пример динамической регистровой процедуры:
4025
 
4026
          : int ABS () /* AX = число, абсолютное значение которого ищется*/
4027
          {
4028
          IF (int AX < 0)
4029
               -AX;
4030
          }
7544 leency 4031
Return to contents.
7496 leency 4032
 
4033
 
7544 leency 4034

7496 leency 4035
    11.4.1 Установка динамической процедуры в определенное место программы.
7544 leency 4036
7496 leency 4037
 
4038
        Динамические процедуры, если они не используются как макросы и если
4039
    они были востребованы в программе, вставляются в код программы в самом
4040
    конце компиляции. В каком точно месте Вашей программы они окажутся узнать
4041
    невозможно. Если же Вам необходимо, чтобы какая-то динамическая процедура
4042
    находилась в конкретном месте программы, то это можно сделать таким
4043
    образом:
4044
 
4045
    :void proc ( int par1, par2)
4046
    {
4047
       ...
4048
    }
4049
 
4050
        Мы имеем динамическую процедуру, код которой был бы расположен ранее
4051
    кода обычной процедуры нашей программы. Для этого перед определением этой
4052
    процедуры надо написать такую строку:
4053
 
4054
    @ void proc ();
4055
 
7544 leency 4056
        В итоге динамическая процедура будет вставлена в код программы не в
7496 leency 4057
    конце ее, как обычно, а в месте, где будет расположена эта строка. Если
4058
    динамическая процедура имеет параметры, то прописывать эти параметры
4059
    необязательно.
4060
 
7544 leency 4061
        В компиляторе есть еще более мощное средство, позволяющее все
7496 leency 4062
    динамические объекты ( процедуры, переменные, структуры ) расположить в
4063
    указанном месте, а не в конце программы, как обычно. Это директива
4064
    #setdinproc. Встретив эту директиву, компилятор немедленно расположит все
4065
    известные ему на этот момент динамические объекты в месте объявления этой
4066
    директивы. Последующие динамические объекты будут располагаться как
4067
    обычно, в конце программы, если конечно, не будет повторно применена
4068
    директива #setdinproc.
4069
 
7544 leency 4070
        Это может быть применено и быть полезным при создании резидентных
7496 leency 4071
    программ (TSR) и драйверов устройств.
7544 leency 4072
Return to contents.
7496 leency 4073
 
4074
 
7544 leency 4075

7496 leency 4076
  11.5 inline-процедуры.
7544 leency 4077
7496 leency 4078
 
4079
      inline-процедурами могут быть динамические процедуры, которые можно
4080
  использовать как макросы. Но в отличие от макросов, inline-процедуры, при
4081
  включенной оптимизации на скорость, автоматически вставляются в код, а при
4082
  оптимизации кода на размер, делается вызов их, как динамических процедур.
4083
 
4084
      Но иногда бывает нужно при включенной оптимизации на размер кода, чтобы
4085
  процедуры вставлялись в код, а не делался их вызов. Для этих целей введена
4086
  директива #inline TRUE. Этой же директивой ( #inline FALSE ), можно при
4087
  оптимизации на скорость делать вызовы процедур, вместо их вставки.
4088
 
4089
      Важно помнить, что статус директивы #inline автоматически меняется при
4090
  смене режима оптимизации. При установке оптимизации на скорость статус
4091
  директивы #inline устанавливается в TRUE, а при смене режима оптимизации по
4092
  размеру кода, устанавливается в FALSE. Поэтому применяйте директиву #inline
4093
  лишь после смены режима оптимизации.
4094
 
4095
      Директивы меняющие режим оптимизации #codesize, #speed и директива
4096
  #inline, объявленные внутри процедуры распространяются только на оставшуюся
4097
  часть процедуры, т.е. они становятся локальными. Для того чтобы изменения
4098
  были глобальными эти директивы надо объявлять вне тела процедуры.
4099
 
4100
      Для того чтобы определить inline-процедуру, надо в первой строке с
4101
  именем процедуры вместо символа динамической процедуры (:) написать
4102
  ключевое слово inline. Пример определения inline-процедуры:
4103
 
4104
  inline int fastcall abs(AX)
4105
  {
4106
      IF ( int AX < 0 ) -AX ;
4107
  }
7544 leency 4108
Return to contents.
7496 leency 4109
 
4110
 
7544 leency 4111

7496 leency 4112
    11.5.1 Другое применение inline.
7544 leency 4113
7496 leency 4114
 
7544 leency 4115
        Ключевое слово inline имеет в процедурах и другое применение. Если
7496 leency 4116
    это слово расположено перед началом блока процедуры, то для такой
4117
    процедуры не создается кадр стека и не генерируется завершающий процедуру
4118
    ret. Пример:
4119
 
4120
    void PROC ()
4121
    inline
4122
    {
4123
      ...
4124
    }
4125
 
7544 leency 4126
        Такие процедуры не должны содержать локальных переменных. Если
7496 leency 4127
    процедура является регистровой (тип fastcall), то с передачей ей
4128
    параметров нет проблем. Если же процедура является стековой, то передать
4129
    в такую процедуру параметры Вы можете, но воспользоваться этими
4130
    параметрами используя их имена, Вы уже не сможете. Это происходит потому,
4131
    что в этих процедурах кадр стека не формируется. Пример:
4132
 
4133
    void proc (int par1, par2)
4134
    inline
4135
    {
4136
      AX=par1; /* компилятор обратится с параметру 'par1' через регистр BP.
7544 leency 4137
                  Но так как кадр стека не был создан, при выполнении этого
7496 leency 4138
                  кода программа будет работать не правильно. */
4139
       ...
4140
    }
4141
 
7544 leency 4142
        Встретив такое определение процедуры, компилятор выдаст предупреждение
7496 leency 4143
    о том, что в таких процедурах использовать локальные и параметрические
4144
    переменные нельзя.
7544 leency 4145
Return to contents.
7496 leency 4146
 
4147
 
7544 leency 4148

7496 leency 4149
  11.6 Процедуры обработки прерываний.
7544 leency 4150
7496 leency 4151
 
4152
      Процедуры обработки прерываний определяются следующим способом:
4153
 
4154
          interrupt procedure_name ()
4155
          {
4156
          // put code here (здесь должен быть код обработки)
4157
          }
4158
 
4159
      Процедуры обработки прерываний не сохраняют никаких регистров
4160
  автоматически, и никакие регистры сами по себе не загружаются перед
4161
  передачей управления обработчику прерывания, следовательно, на Вашей
4162
  совести сохранение значений регистров в стеке и последующий их возврат, а
4163
  также загрузка регистра DS нужным значением.
4164
 
4165
      Вот пример обработчика прерывания, который сохраняет значения всех
4166
  регистров и загружает регистр DS:
4167
 
4168
       interrupt safe_handle ()
4169
       {
4170
       $ PUSH DS
4171
       $ PUSH ES
4172
       $ PUSHA   // для выполнения этой команды нужен процессор не хуже 80286
4173
       DS = CS;  // здесь DS загружается для работы с моделью памяти типа tiny
4174
 
4175
 
4176
       /* do your thing here (здесь вы делаете свою обработку)*/
4177
 
4178
       $ POPA   // для выполнения этой команды нужен процессор не хуже 80286
4179
       $ POP ES
4180
       $ POP DS
4181
       }
4182
 
4183
      При завершении процедуры прерывания будет автоматически сгенерирована
4184
  инструкция выхода из обработчика прерывания - IRET.
7544 leency 4185
Return to contents.
7496 leency 4186
 
4187
 
7544 leency 4188

7496 leency 4189
  11.7 Замена return на goto.
7544 leency 4190
7496 leency 4191
 
4192
      В некоторых ситуациях, при компиляции программы, оператор return
4193
  будет заменяться на goto. Это происходит при разрешенной оптимизации по
4194
  размеру кода для операторов return, которые расположены внутри процедуры
4195
  и, естественно, если размер кода для выполнения return больше, чем размер
4196
  кода для реализации goto. Для динамических процедур, которые используются
4197
  как макросы, такая замена будет производится всегда. Оператор goto будет
4198
  выполнен на конец процедуры, там, где будет располагаться единственный
4199
  выход из процедуры. В динамических процедурах, используемых в качестве
4200
  макросов, return в конце процедуры будет пропущен компилятором.
4201
 
4202
      Таким образом, снято последнее ограничение на использование
4203
  динамических процедур в качестве макросов. Любая динамическая процедура
4204
  может быть использована как макрос.
4205
 
4206
      Для оператора goto существует его более короткий аналог - GOTO.
4207
  Для получения более компактного кода для оператора return введен также
4208
  более короткий оператор RETURN. Его можно использовать, если от места
4209
  его применения до конца процедуры находится не более 128 байт. Если Вы
4210
  будете использовать RETURN на большем расстоянии до конца процедуры, то
4211
  компилятор выдаст сообщение об ошибке. При использовании return на
4212
  расстоянии меньше 128 байт до конца кода, компилятор выдаст вам
4213
  предупреждение о возможном использовании RETURN.
7544 leency 4214
Return to contents.
7496 leency 4215
 
4216
 
7544 leency 4217

7496 leency 4218
  11.8 Возвращаемые значения.
7544 leency 4219
7496 leency 4220
 
4221
      Возвращаемые из функций значения располагаются в регистрах. В таблице
4222
  показано, какой регистр используется для каждого из возвращаемых типов:
4223
 
4224
      --------------------------------------------
4225
      | возвращаемый тип |  используемый регистр |
4226
      --------------------------------------------
4227
      |        byte      |        AL             |
4228
      |        word      |        AX             |
4229
      |        dword     |        EAX            |
4230
      |        char      |        AL             |
4231
      |        int       |        AX             |
4232
      |        long      |        EAX            |
4233
      |        float     |        EAX            |
4234
      --------------------------------------------
4235
 
4236
      Самый простой способ вернуть значение из функции состоит в том, чтобы
4237
  использовать команду return(), но вместо этого можно напрямую загрузить
4238
  возвращаемое значение в соответствующий регистр. Например, следующие две
4239
  функции возвращают одно и то же значение:
4240
 
4241
           byte proc_one ()
4242
           {
4243
           return (42);
4244
           }
4245
 
4246
           byte proc_two ()
4247
           {
4248
           AL = 42;
4249
           }
4250
 
4251
      Многие DOS функции 0x21 прерывания в качестве индикатора успешного
4252
  выполнения используют установку или сброс carry флага. Использовать флаги
4253
  процессора при возврате из процедур можно и в других случаях, когда надо
4254
  иметь статус успешного или не успешного выполнения процедуры. Это позволит
4255
  более полно использовать возможности процессора и соответственно уменьшит
4256
  размер кода и повысит быстродействие программы.
4257
 
4258
      Наряду с флагами, при возврате из процедур, по прежнему остается
4259
  возврат различных типов и через регистр AL/AX/EAX. Если для процедуры
4260
  объявлено, что она имеет тип возврата int и CARRYFLAG, то при использовании
4261
  такой процедуры в операциях сравнения IF, WHILE... будет делаться проверка
4262
  carry флага, а не сравнение регистра AX. Пример использования возврата
4263
  флагов из процедур:
4264
 
7544 leency 4265
  int CARRYFLAG FOPEN();        // объявление процедуры
7496 leency 4266
 
4267
  void proc()
4268
  {
4269
    IF ( FOPEN(name,0) ) Error ( "Not open file" );
4270
  }
4271
 
4272
      Варианты допустимого синтаксиса для использования возврата флага:
4273
 
4274
  IF ( ! FOPEN() )...
4275
  IF ( @ FOPEN() )...
4276
  IF ( ! @ FOPEN() )...
4277
  IF ( handl = FOPEN() )...
4278
  IF ( handl = @ FOPEN() )...
4279
  IF ( ! handl = FOPEN() )...
4280
  IF ( ! handl = @ FOPEN() )...
4281
 
4282
      А вот варианты, в которых, несмотря на то, что для процедуры объявлен
4283
  возврат флага, будет производиться сравнение регистра AX:
4284
 
7544 leency 4285
  IF ( FOPEN() == 5 )...        // производится сравнение
7496 leency 4286
  IF ( FOPEN() + 2 )...   // результат процедуры подвергается дальнейшему
4287
                          // вычислению, в результате которого флаги будут
7544 leency 4288
                        // изменены.
4289
Return to contents.
7496 leency 4290
 
4291
 
7544 leency 4292

7496 leency 4293
  11.9 Объявление параметров в регистровых процедурах.
7544 leency 4294
7496 leency 4295
 
4296
      Ранее каждому параметру регистровой процедуры соответствовал строго
4297
  определенный регистр. Например, для переменных типа int или word первый
4298
  параметр передавался через регистр AX, 2-й - BX, 3-й - CX, 4-й - DX, 5-й -
4299
  DI, 6-й - SI. Поэтому, если Вам было необходимо передать только один
4300
  параметр через регистр SI, то приходилось перед ним писать пять запятых.
4301
  Вот как, например, выглядит вызов процедуры STRCPY:
4302
 
4303
  void main ()
4304
  {
4305
    STRCPY ( , , , , #dest, #sourc ) ;
4306
  }
4307
 
4308
      Теперь регистры могут располагаться при передаче параметров
4309
  произвольным образом. Надо только объявить компилятору о том, какой регистр
4310
  закреплен за каким параметром данной процедуры. После такого объявления
4311
  компилятор будет сам следить за тем, через какой регистр передавать
4312
  параметр процедуре, его размерностью и числом передаваемых параметров. Вот
4313
  как будет выглядеть объявление и использование процедуры STRCPY:
4314
 
7544 leency 4315
  void STRCPY ( DI, SI ) ;      //это объявление процедуры
7496 leency 4316
 
4317
  void main ()
4318
  {
7544 leency 4319
    STRCPY ( #dest, #sourc ) ;  //а это вызов процедуры
7496 leency 4320
  }
4321
 
4322
      Можно не делать объявления процедуры, а указать расположение регистров
4323
  в заголовке процедуры. Но тогда такая процедура должна вызываться только
4324
  после ее определения. Вот пример процедуры выводящей на экран несколько
4325
  одинаковых символов:
4326
 
4327
  void PUTNCHAR(AL,CX,BL,BH)
4328
  /* 1 параметр в AL - код символа, который будет выведен
4329
     2 параметр в CX - число выводимых символов
4330
     3 параметр в BL - цветовой атрибут
4331
     4 параметр в BH - номер видеостраницы
4332
  */
4333
  {
4334
    AH=9;
4335
    $INT 0x10
4336
  }
4337
 
4338
      При объявлении регистровой процедуры можно также указывать какой тип
4339
  переменной ожидает процедура (знаковый/без знаковый или вещественный). По
4340
  умолчанию считается без знаковый тип. Однако знаковый тип указывать есть
4341
  смысл только если параметр передается через регистр AL/AX/EAX. Через другие
4342
  регистры переменная всегда передается как без знаковая. Пример объявления
4343
  регистровой процедуры с указанием типов:
4344
 
4345
  int fastcall Exampl( word CX, int AX, DX, float ESI ) ;
4346
   |    |        |        |         |   |   |
4347
   |    |        |        |         |   |   |---- 4-й парам. имеет тип float и
4348
   |    |        |        |         |   |         перед. через регистр ESI.
4349
   |    |        |        |         |   |-------- 3-й парам. имеет по умолч.
4350
   |    |        |        |         |             тип word и перед. через DX.
4351
   |    |        |        |         |------------ 2-й парам. имеет тип int и
4352
   |    |        |        |                       передается через регистр AX.
4353
   |    |        |        |---------------------- 1-й парам. имеет тип word и
4354
   |    |        |                                передается через регистр CX.
4355
   |    |        |------------------------------- Имя объявляемой процедуры.
4356
   |    |---------------------------------------- Модификатор, указывающий, что
4357
   |                                              эта проц. явл. регистровой.
4358
   |--------------------------------------------- Процедура возвращает перемен.
7544 leency 4359
                                                                          типа int.
7496 leency 4360
 
4361
      Если Вы сделали объявление регистров процедуры, то компилятор будет
4362
  строго следить за количеством указанных параметров при вызове этой
4363
  процедуры и выдавать сообщения об ошибке, если их будет меньше или больше.
4364
  С одной стороны это хорошо - есть контроль за тем, что Вы ничего не забыли
4365
  или не добавили лишнего при вызове процедуры. С другой стороны иногда
4366
  бывают необязательные параметры, а их теперь придется прописывать. Но если
4367
  Вы при вызове процедуры не укажете ни одного параметра, то компилятор не
4368
  будет Вам выдавать сообщение об ошибке.  Это дает Вам возможность
4369
  проинициализировать регистры, через которые Вы передаете параметры, вне
4370
  вызова процедуры.  Но если Вы укажете, хоть один параметр, то Вам придется
4371
  указывать и остальные, иначе компилятор будет считать, что Вы их случайно
4372
  пропустили и выдаст сообщение об ошибке.
4373
 
4374
      Если Вы не объявили регистры ни при объявлении регистровой процедуры,
4375
  ни в заголовке самой процедуры, то компилятор будет считать, что параметры
4376
  в эту процедуру передаются старым способом. Таким образом, достигается
4377
  полная совместимость с предыдущими версиями компилятора.
7544 leency 4378
Return to contents.
7496 leency 4379
 
4380
 
7544 leency 4381

7496 leency 4382
  11.10 Объявление параметров в стековых процедурах.
7544 leency 4383
7496 leency 4384
 
4385
      Как известно, ранее в C-- контроль за числом и типом передаваемых
4386
  процедуре параметров возлагался на программиста. Поэтому возникла непростая
4387
  задача, совместить одновременно отсутствие контроля за параметрами (для
4388
  совместимости с предыдущими версиями) и ее наличие. В результате
4389
  компромиссов появился вариант немного отличающийся от традиционно принятого
4390
  в языках C.
4391
 
4392
      Главное отличие - это то, что параметры, определяемые при определении
4393
  процедуры, не будут восприниматься компилятором для контроля за ними. Во
4394
  всех языках C допускается совмещение прототипа процедуры и ее объявления.
4395
  В C-- для того, чтобы включился контроль за параметрами стековой процедуры,
4396
  надо эту процедуру обязательно объявить. Но не всякое объявление процедуры
4397
  будет сигналом компилятору о включении контроля за параметрами этой
4398
  процедуры. Если при объявлении в круглых скобках ничего не будет, то
4399
  компилятор не будет отслеживать параметры, передаваемые этой процедуре. В
4400
  C++ такое объявление означает, что процедуре не передаются никакие
4401
  параметры. В C-- для этого надо при объявлении процедуры в круглых скобках
4402
  обязательно написать void. Например:
4403
 
4404
  int proc ( void ) ;
4405
 
4406
      Встретив такое объявление процедуры, компилятор будет следить за тем,
4407
  чтобы этой процедуре не были переданы параметры.
4408
 
4409
      При объявлении процедуры имена параметров можно опускать. Как известно,
4410
  в C-- параметры процедуры одного типа записываются через запятую. Для смены
4411
  типа используют точку с запятой. При объявлении смену типа можно
4412
  производить и после запятой:
4413
 
4414
  void ptoc ( int a, b, c; word d );
4415
  void proc ( int, int, int, word );
4416
  void proc ( int, int, int; word );
4417
 
4418
      Все эти примеры объявлений являются идентичными и допустимыми.
4419
 
4420
      Для контроля за процедурами с переменным числом параметров был введен
4421
  новый для C-- элемент синтаксиса - многоточие или его еще называют эллипс.
4422
  Вот как будет выглядеть объявление процедуры printf:
4423
 
4424
  void cdecl printf ( word, ... );
7544 leency 4425
Return to contents.
7496 leency 4426
 
4427
 
7544 leency 4428

7496 leency 4429
  11.11 Использование макрокоманд.
7544 leency 4430
7496 leency 4431
 
4432
      Теперь любая динамическая процедура может быть использована как макрос.
4433
  Если перед вызовом динамической процедуры поставить символ @, то код этой
4434
  процедуры будет вставлен, а не вызван инструкцией CALL.
4435
 
4436
      При использовании стековых динамических процедур в качестве макросов
4437
  очистка стека от переданных параметров производится ассемблерной
4438
  инструкцией ADD SP,SIZE_PARAMETRS сразу после окончания кода вставленного
4439
  макроса. Поэтому, если эта процедура использовала флаги в качестве
4440
  возврата, то они будут разрушены.
7544 leency 4441
Return to contents.
7496 leency 4442
 
4443
 
7544 leency 4444

7496 leency 4445
  11.12 Передача параметров в стековые процедуры через регистры.
7544 leency 4446
7496 leency 4447
 
4448
      При передаче параметров через регистры, чаще всего получается более
4449
  компактный и быстрый код. Но содержимое регистров может быть легко
4450
  разрушено. Если в Вашей процедуре, какой-то из параметров используется
4451
  однократно для того, чтобы в начале процедуры инициализировать какой-то
4452
  регистр, то Вы можете передать это значение в процедуру сразу через
4453
  регистр, минуя стадию засовывания и извлечения содержимого в стек. Пример:
4454
 
4455
  int proc (int param1, param2, param3)
4456
  {
4457
    (E)BX = param3;
4458
    (E)BX.TEG_STRUCT.var = proc2 (param1,papra2);
4459
    proc3 (param1,param2);
4460
  }
4461
 
4462
      В этом примере параметр param3 используется лишь для того, чтобы
4463
  инициализировать регистр (E)BX, поэтому его можно сразу передать через
4464
  регистр:
4465
 
4466
  int proc (int param1, param2, (E)BX)
4467
  {
4468
    (E)BX.TEG_STRUCT.var = proc2 (param1,papra2);
4469
    proc3 (param1,param2);
4470
  }
4471
 
4472
      Как Вы видите, процедура немного упростилась.
4473
 
4474
      В принципе, порядок расположения стековых и регистровых параметров не
4475
  принципиален. Но надо помнить, что содержимое регистров может быть легко
4476
  разрушено, и поэтому лучше всего регистровые параметры инициализировать
4477
  лишь после того, как были засунуты в стек все стековые параметры. Для
4478
  процедур типа pascal регистровые параметры лучше располагать после
4479
  стековых параметров. Для процедур типа cdecl и stdcall сначала лучше
4480
  располагать регистровые параметры.
7544 leency 4481
Return to contents.
7496 leency 4482
 
4483
 
7544 leency 4484

7496 leency 4485
  11.13 Вызов процедур с адресом в регистре.
7544 leency 4486
7496 leency 4487
 
4488
      В C-- допустимо делать вызов процедуры, адрес которой находится в
4489
  регистре. Параметры для такого вызова передаются только через стек. Тип
4490
  вызова процедуры для программ под Windows stdcall, для остальных pascal.
4491
  Адрес процедуры для 32-битных программ должен находится в 32-битном
4492
  регистре, а для 16-битных программ в 16-битном регистре. Считается, что
4493
  такой вызов имеет возврат типа unsigned int. Пример:
4494
 
4495
    BX = # proc;
4496
    BX (a);
4497
    IF ( BX(b) == 0 ) AX=2;
4498
 
4499
    Вы получите следующий код:
4500
 
4501
  test.c-- 8: BX=#proc;
4502
  0104 BB1A01                   mov     bx,11Ah
4503
 
4504
  test.c-- 9: BX(a);
4505
  0107 FF76FC                   push    word ptr [bp-4]
4506
  010A FFD3                     call    near bx
4507
 
4508
  test.c-- 10: IF (BX(b) == 0)AX=2;
4509
  010C FF76FE                   push    word ptr [bp-2]
4510
  010F FFD3                     call    near bx
4511
  0111 85C0                     test    ax,ax
4512
  0113 7503                     jne     118h
4513
  0115 B80200                   mov     ax,2
7544 leency 4514
Return to contents.
7496 leency 4515
 
4516
 
7544 leency 4517

7496 leency 4518
  11.14 Встроенные в компилятор процедуры.
7544 leency 4519
7496 leency 4520
 
4521
      Для некоторых процедур Вы не найдете их исходные тексты в библиотеках
4522
  компилятора. Код этих процедур генерирует компилятор. Вот список этих
4523
  процедур:
4524
 
4525
  ABORT             Прекращение выполнения программы
4526
  atan              Вычислить арктангенс числа
4527
  atan2             Вычислить арктангенс числа
4528
  ATEXIT            Зарегистрировать функцию выполняющуюся при выходе.
4529
  cos               Возвращает косинус угла
4530
  EXIT              Закончить программу с кодом ошибки
4531
  exp               Возвращает экспоненту числа
4532
  inp/inportb       Считать один байт из порта
4533
  inport            Считать слово из порта
4534
  inportd           Считать двойное слово из порта
4535
  fabs              Возвращает абсолютное значение числа
4536
  log               Вычисляет натуральный логарифм числа
4537
  log10             Вычисляет десятичный логарифм числа
4538
  outp/outportb     Записать один байт в порт
4539
  outport           Записать слово в порт
4540
  outportd          Записать двойное слово в порт
4541
  sin               Возвращает синус угла
4542
  sqrt              Извлечь квадратный корень через FPU.
4543
  tan               Возвращает тангенс угла
4544
 
4545
      Размещение этих процедур непосредственно в компиляторе, связано с тем,
4546
  что в настоящий момент компилятор может таким образом генерировать более
4547
  эффективный код, чем если бы эти процедуры располагались в библиотеках.
4548
  В будущем, по мере развития компилятора, эти процедуры постепенно будут
4549
  выносится из компилятора в библиотеки.
4550
 
4551
      Но ничто не мешает Вам уже сейчас написать свои одноименные
4552
  библиотечные процедуры. Встретив определение такой процедуры, компилятор не
4553
  будет выдавать никаких сообщение, он просто будет применять Ваш вариант
4554
  процедуры.
7544 leency 4555
Return to contents.
7496 leency 4556
 
4557
 
7544 leency 4558

7496 leency 4559
    11.14.1 Процедуры ABORT, ATEXIT и EXIT.
7544 leency 4560
7496 leency 4561
 
4562
        Процедуры ABORT и EXIT связаны с работой директивы #atexit и
4563
    процедурой ATEXIT. Наиболее оптимальную их реализацию и взаимную
4564
    интеграцию может сделать только компилятор. Именно поэтому эти процедуры
4565
    поддерживаются компилятором.
4566
 
4567
        Процедура ATEXIT - регистровая процедура, которая регистрирует
4568
    функцию, адрес которой передается ей в качестве параметра, т.е. через
4569
    регистр (E)AX, как функцию завершения программы. При успешной регистрации
4570
    ATEXIT возвращает 0. Всего можно зарегистрировать до 16 функций.
4571
 
4572
        Завершающие функции не должны иметь параметров и возврата. Эти
4573
    функции будут выполняться в порядке обратном очередности регистрации в
4574
    случае, если Вы будете завершать работу программы через вызовы процедур
4575
    ABORT или EXIT или закончится работа процедуры main. Если Вы
4576
    завершите работу программы вызовом процедуры ExitProcess под Windows или
4577
    вызовом AH=0x4C; $int 0x21 под DOS, выход из программы произойдет без
4578
    запуска зарегистрированных функций.
4579
 
4580
        Процедура ABORT и EXIT, если не включена директива #atexit делают
4581
    вызов процедуры ExitProcess под Windows и вызов AH=0x4C; $int 0x21 под
4582
    DOS.  Процедуре ABORT не передаются никакие параметры, и она завершает
4583
    работу программы с кодом возврата 0. Процедуре EXIT передается в
4584
    качестве параметра код возврата, с которым она и завершает работу
4585
    программы.
7544 leency 4586
Return to contents.
7496 leency 4587
 
4588
 
7544 leency 4589

7496 leency 4590
    11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и
4591
                                                                    outportd
7544 leency 4592
7496 leency 4593
 
4594
        Эти процедуры всегда вставляются в код как макросы, т.е. для этих
4595
    процедур никогда не генерируется вызов процедуры. В зависимости от
4596
    значения порта, с которым работают эти процедуры, генерируется разный
4597
    код. Все это позволяет получать более компактный код.
4598
 
4599
        Процедуры чтения из порта имеют такой прототип:
4600
 
4601
    byte inp ( word port );
4602
    word inport ( word port );
4603
    dword inportd ( word port );
4604
 
4605
        Процедуры записи в порт имеют такой прототип:
4606
 
4607
    void outp ( byte val; word port );
4608
    void outport ( word val; word port );
4609
    void outportd ( dword val; word port );
4610
 
4611
        Имена процедур inp и inportb, также как и имена outp и outportb
4612
    являются синонимами.
7544 leency 4613
Return to contents.
7496 leency 4614
 
4615
 
7544 leency 4616

7496 leency 4617
    11.14.3 Процедуры для работы с вещественными числами.
7544 leency 4618
7496 leency 4619
 
4620
        Эти процедуры реализуются компилятором и всегда вставляются в код как
4621
    макросы, т.е. для них никогда не генерируется вызов процедуры. Кроме
4622
    этого, если параметром одной процедуры является вызов другой, то
4623
    результат работы второй процедуры остается в стеке FPU, а первая
4624
    процедура использует этот результат непосредственно из стека. Таким
4625
    образом получаются более компактный код. Вот вымышленный пример:
4626
 
4627
    test.c-- 7: f = sin( sqrt(1) );
4628
    0100 D9061C01                 fld     [11Ch]
4629
    0104 D9FA                     fsqrt
4630
    0106 D9FE                     fsin
4631
    0108 D91E2001                 fstp    [120h]
4632
    010C 9B                       fwait
4633
 
4634
        Эти процедуры имеют следующий прототип:
4635
 
4636
    float atan ( float val );
4637
    float atan ( float val, val2 );
4638
    float cos ( float val );
4639
    float exp ( float val );
4640
    float fabs ( float val );
4641
    float log ( float val );
4642
    float log10 ( float val );
4643
    float sin ( float val );
4644
    float sqrt ( float val );
4645
    float tan ( float val );
7544 leency 4646
Return to contents.
7496 leency 4647
 
4648
 
7544 leency 4649

7496 leency 4650
  11.15 Классы.
4651
 
4652
    11.15.1 Объявление процедур в структурах.
7544 leency 4653
7496 leency 4654
 
4655
        С введение поддержки объявления процедур в структурах, структура
4656
    становится подобной классу в C++. Т.е. такая процедура становится методом
4657
    класса. Пример:
4658
 
4659
    struct Point  // объявление класса
4660
    {
7544 leency 4661
        int x; // элементы данных
4662
        int y; // класса типа Point
4663
        void SetX(int);  // объявление методов
4664
        void SetY(int);  // класса Point
7496 leency 4665
    };
4666
 
4667
    void Point::SetX(int _x)  //определение процедуры класса Point
4668
    {
7544 leency 4669
        IF((_x>=0)&&(_x<=MAX_X)) x=_x;
7496 leency 4670
    // переменные x, y являются членами этого класса и поэтому доступ к ним из
4671
    // процедур этого же класса осуществляется напрямую.
4672
     }
4673
 
4674
    void main()
4675
    Point p;  //определяем структуру в стеке
4676
    {
4677
      p.y = p.x = 0;
4678
      p.SetX(1);
4679
    }
4680
 
4681
        При вызове процедуры являющейся методом класса ей неявным образом
4682
    передается адрес этого класса (структуры). В самой процедуре этот адрес
4683
    доступен через имя параметрической переменной this. Эту переменную
4684
    автоматически генерирует компилятор. Если в объявление процедуры в
4685
    структуре указать ключевое слово static, то такой процедуре адрес
4686
    класса не передается и переменная this не генерируется.
4687
 
4688
        Процедура объявленная в структуре может быть динамической. Для этого,
4689
    при ее определении, в самом ее начале, надо написать символ двоеточия :
4690
    (также как и для обычных динамических процедур). Но такая динамическая
4691
    процедура не может быть использована как макрос.
7544 leency 4692
Return to contents.
7496 leency 4693
 
4694
 
7544 leency 4695

7496 leency 4696
    11.15.2 Наследование.
7544 leency 4697
7496 leency 4698
 
4699
        В C-- поддерживаются простые и множественные наследования. Объявление
4700
    структуры с наследованием имеет следующий синтаксис:
4701
 
4702
    struct Derived : Base1, Base2, ... Basen
4703
    {
4704
      int x0;
4705
    };
4706
 
4707
        Число базовых структур в производном не ограничено. При множественном
4708
    наследовании структура может наследовать два и более экземпляра базовой
4709
    структуры. При этом возникает неоднозначность. Пример:
4710
 
4711
    struct A
4712
    {
4713
      int x,y;
4714
      . . .
4715
    };
4716
 
4717
    struct B : A  //структура B наследует A
4718
    {
4719
      . . .
4720
 
4721
    };
4722
 
4723
    struct C : A  //структура C наследует A
4724
    {
4725
      . . .
4726
    };
4727
 
4728
    struct D : B, C //структура D наследует B и C
4729
    {
4730
      . . .
4731
    };
4732
 
4733
    void main()
4734
    D d;  //выделяем для структуры D память в стеке и присваиваем ей имя d
4735
    {
4736
      d.x0=0;
4737
 
4738
        В этом примере структура D наследует два экземпляра структуры A и
4739
    в ней находятся два элемента с именем x0. Компиляторы C++ при записи
4740
    типа d.x0=0 выдают сообщение об ошибке. C-- эту запись обрабатывает,
4741
    присваивание производится по умолчанию в элемент из последней базовой
4742
    структуры, имеющей элемент x0. Для того чтобы получить доступ ко
4743
    второму элементу x0 (физически этот элемент находится в структуре
4744
    первым), необходимо применить операцию разрешения видимости:
4745
 
4746
      d.B::x0=0;
4747
 
4748
        Из всего этого следует, что записи:
4749
 
4750
      d.x0=0;
4751
    и
4752
      d.C::x0=0;
4753
 
4754
         являются равнозначными.
7544 leency 4755
Return to contents.
7496 leency 4756
 
4757
 
7544 leency 4758

7496 leency 4759
    11.15.3 Наследование процедур.
7544 leency 4760
7496 leency 4761
 
7544 leency 4762
        Если в базовом классе есть процедура, а в производном классе Вы эту
7496 leency 4763
    процедуру переопределили, то эта процедура будет переопределена и в
4764
    базовом классе. Таким образом процедура определенная в базовом классе
4765
    будет потеряна. Пример:
4766
 
4767
    struct Point  // базовый класс
4768
    {
7544 leency 4769
        int x; // элементы данных
4770
        int y; // класса типа Point
4771
        void SetX(int);  // объявление методов
4772
        void SetY(int);  // класса Point
7496 leency 4773
    };
4774
 
4775
    void Point::SetX(int _x)  // определение процедуры класса Point
4776
    {
7544 leency 4777
        IF((_x>=0)&&(_x<=MAX_X)) x=_x;
7496 leency 4778
    }
4779
 
4780
    struct Point2 : Point  // производный класс
4781
    {
4782
      int x2;
4783
    }
4784
 
4785
    struct Point3 : Point  // еще один производный класс
4786
    {
4787
      int z;
4788
    }
4789
 
4790
    void Point3::SetX(int _x)  // в этом производном классе переопределяем
4791
    {                          // процедуру SetX
7544 leency 4792
        IF((_x>=80)&&(_x<=MAX_X)) x=_x;
7496 leency 4793
    }
4794
 
7544 leency 4795
        Процедура SetX, определенная в базовом классе Point, теперь будет
7496 leency 4796
    недоступна. Вместо кода определенного в этом классе, будет вызываться код
4797
    процедуры, определенный в наследуемом классе Point3. При вызове процедуры
4798
    SetX из другого производного класса Point2 будет также вызываться код
4799
    процедуры, определенный в производном классе Point3. Переопределяя
4800
    процедуру таким образом, Вы замените код этой процедуры в базовом классе и
4801
    во всех его наследуемых классах.
4802
 
7544 leency 4803
        Если Вам необходимо, чтобы код новой процедуры был доступен
7496 leency 4804
    одновременно с кодом старой процедуры, то в производном классе Вам
4805
    необходимо сделать еще одно объявление этой процедуры. Пример:
4806
 
4807
    struct Point  // базовый класс
4808
    {
7544 leency 4809
        int x; // элементы данных
4810
        int y; // класса типа Point
4811
        void SetX(int);  // объявление методов
4812
        void SetY(int);  // класса Point
7496 leency 4813
    };
4814
 
4815
    void Point::SetX(int _x)  // определение процедуры класса Point
4816
    {
7544 leency 4817
        IF((_x>=0)&&(_x<=MAX_X)) x=_x;
7496 leency 4818
    }
4819
 
4820
    struct Point2 : Point  // производный класс
4821
    {
4822
      int x2;
4823
    }
4824
 
4825
    struct Point3 : Point  // еще один производный класс
4826
    {
4827
      int z;
4828
      void SetX(int);  // в наследуемом классе делаем еще одно объявление
4829
                       // процедуры SetX
4830
    }
4831
 
4832
    void Point3::SetX(int _x)  // в этом производном классе переопределяем
4833
    {                          // процедуру SetX
7544 leency 4834
        IF((_x>=80)&&(_x<=MAX_X)) x=_x;
4835
        EDI=this;
4836
        EDI.Point.SetX(_x);  // делаем вызов одноименной процедуры из
4837
                             // базового класса
7496 leency 4838
    }
4839
 
7544 leency 4840
        Теперь из производного класса Point3 Вам доступны две различные
7496 leency 4841
    процедуры с одним именем SetX. А из базового класса Point и из другого
4842
    производного класса Point2 будет по прежнему доступен только базовый
4843
    вариант процедуры SetX.
7544 leency 4844
Return to contents.
7496 leency 4845
 
4846
 
7544 leency 4847

7496 leency 4848
12. Типы выходных файлов.
4849
 
4850
  12.1 Выходные файлы типа COM.
7544 leency 4851
7496 leency 4852
 
4853
      Этот тип выходного файла получается автоматически по умолчанию.
4854
 
4855
      Изначально C-- мог делать только файлы формата типа COM. В настоящее
4856
  время появилась возможность получать файла типа EXE с моделями памяти tiny
4857
  и small для 16-битного кода, а также 32-битные для DOS и Windows. Также
4858
  есть возможность получения выходного файла в формате OBJ, что позволяет
4859
  связывать программы на C-- с программами на других языках.
7544 leency 4860
Return to contents.
7496 leency 4861
 
4862
 
7544 leency 4863

7496 leency 4864
  12.2 Выходные файлы типа EXE.
7544 leency 4865
7496 leency 4866
 
4867
      Этот формат файла можно получить, если компилировать с ключом командной
4868
  строки /exe или /e.
4869
 
4870
      Возможно также поддержка EXE-формата через выходной файл формата OBJ,
4871
  который можно затем обработать линковщиком, не входящим в пакет C--.
7544 leency 4872
Return to contents.
7496 leency 4873
 
4874
 
7544 leency 4875

7496 leency 4876
  12.3 Выходной файл *.EXE с моделью памяти tiny.
7544 leency 4877
7496 leency 4878
 
4879
      Фактически код файла *.exe модели tiny ничем не отличается от кода
4880
  *.com. В сущности, это тот же com-файл, к которому добавлен 32-байтный
4881
  заголовок exe-файла. Единственное отличие возникает, когда Вы компилируете
4882
  файл с директивой ?resize TRUE. В com-файле, по этой директиве, в код
4883
  программы добавляется соответствующий код, изменяющий размер доступной
4884
  памяти. В exe-файле для этих целей будет скорректирован заголовок
4885
  exe-файла.
4886
 
4887
      Чтобы получить exe-файл с моделью памяти tiny, надо запустить
4888
  компилятор с ключом в командной строке /TEXE.
7544 leency 4889
Return to contents.
7496 leency 4890
 
4891
 
7544 leency 4892

7496 leency 4893
  12.4 Объектный выходной файл OBJ.
7544 leency 4894
7496 leency 4895
 
4896
      В настоящее время C-- может только создавать OBJ-файлы, но не может их
4897
  компоновать.
4898
 
4899
      Ранее C-- создавал obj-файлы, которые могли быть подключены к проектам
4900
  созданным на других языках, т.е. ведомые (slave) модули. Причем из C--
4901
  модулей для основного проекта были доступны только процедуры и эти
4902
  процедуры не должны были использовать глобальные переменные.
4903
 
4904
      Теперь же C-- может создавать основной модуль (master), который может
4905
  быть слинкован в самостоятельный файл.
4906
 
4907
      Для obj-файлов появилась возможность использовать внешние (extern)
4908
  процедуры, переменные или структуры. Для этого достаточно их объявить как
4909
  extern. Причем ключевое слово extern должно быть всегда первым. Пример
4910
  объявления внешних объектов:
4911
 
4912
  extern void cdecl _printf(); // объявление внешней процедуры _printf имеющей
4913
                               // тип cdecl  и тип возврата void
4914
  extern int buts,cubs;        // объявление двух внешних переменных типа int
4915
  extern struct IPXL ipxl;     // объявление внешней структуры ipxl имеющей тег
7544 leency 4916
                             // IPXL,  причем тег этой структуры должен быть
4917
                             // описан ранее.
7496 leency 4918
 
4919
      Появление возможности объявлять внешние объекты позволяет подключать к
4920
  obj-модулю на C-- модули написанные на других языках или подключать к
4921
  программе на C-- процедуры из библиотек на других языках. При объявлении
4922
  внешних объектов очень важно правильно указать тип процедуры и ее имя. Если
4923
  Вы будете использовать внешние процедуры, написанные на C то чаще всего,
4924
  Вам нужно будет указывать модификатор cdecl, а к имени процедуры или
4925
  переменной добавлять префикс _.
4926
 
4927
      Из основного (master) obj-файла написанного на C-- для других
4928
  obj-модулей доступны все процедуры, глобальные переменные и глобальные
4929
  структуры.
4930
 
4931
      Чтобы получить ведомый obj-модуль при компиляции надо использовать ключ
4932
  /sobj.
4933
 
4934
      C-- может создавать obj-файлы с моделью памяти tiny и small. По
4935
  умолчанию создаются модули с моделью tiny. Чтобы получить obj-файл с
4936
  моделью памяти small надо запустить компилятор с ключами /obj и /exe.
4937
 
4938
      Для создания obj-файлов для 32-битного DOS в командной строке Вам
4939
  необходимо указать ключи /d32 и /obj. Использовать полученный obj-файл мне
4940
  удалось лишь с помощью wlink и расширителя zrdx.exe.
4941
 
4942
      Создание obj-файлов под windows не предусмотрено.
7544 leency 4943
Return to contents.
7496 leency 4944
 
4945
 
7544 leency 4946

7496 leency 4947
  12.5 COM файл symbiosis.
4948
 
4949
    12.5.1 СИМБИОЗ - что это такое?
7544 leency 4950
7496 leency 4951
 
4952
        Транслятор C-- имеет ключ, позволяющий добавлять компилируемую
4953
    программу к концу уже имеющегося COM файла. Это называют COM-файл
4954
    Symbiosis. Когда такая программа запускается, управление сначала получает
4955
    добавленный код C--, и только после выполнения его процедуры main()
4956
    управление получит первоначальный код COM-файла.
4957
 
4958
        Если добавленный вами код завершается EXIT() или ABORT(), программа
4959
    прекратится, и первоначальный код COM-файла не будет выполнен. Это
4960
    позволяет программе, добавленной к COM файлу, определять, будет ли
4961
    управление передано на первоначальный код.
7544 leency 4962
Return to contents.
7496 leency 4963
 
4964
 
7544 leency 4965

7496 leency 4966
    12.5.2 Как это делать.
7544 leency 4967
7496 leency 4968
 
4969
        Чтобы сделать это, Вы должны использовать ключ /SYM в командной
4970
    строке компилятора, в которой указывается полное имя COM-файла, к
4971
    которому что-то добавляется. При этом оригинал COM-файла не меняется, а
4972
    новый файл содержит его в себе. Например, чтобы откомпилировать программу
4973
    HELLO.C-- к концу копии C:\command.сом используют следующую команду:
4974
 
4975
             C-- /SYM C:\COMMAND.COM HELLO.C--
4976
 
4977
    Будет создан выходной файл HELLO.COM .
7544 leency 4978
Return to contents.
7496 leency 4979
 
4980
 
7544 leency 4981

7496 leency 4982
    12.5.3 Использование.
7544 leency 4983
7496 leency 4984
 
4985
        Вы можете, вероятно, придумать большое количество путей использования
4986
    этой функции, типа:
4987
 
4988
             - Добавление защиты с использованием пароля к некоторым
7544 leency 4989
               специальным COM файлам.
7496 leency 4990
             - Уменьшение памяти, доступной COM файлу при запуске.
4991
             - Инициализация режима видео для COM файла.
7544 leency 4992
Return to contents.
7496 leency 4993
 
4994
 
7544 leency 4995

7496 leency 4996
    12.5.4 Злоупотребления.
7544 leency 4997
7496 leency 4998
 
4999
        Любой злоумышленник может придумать и вредные применения для этой
5000
    функции. Наиболее очевидное из них - создание троянских коней. Я хотел бы
5001
    указать, что это неконструктивное использование C--, и любое
5002
    разрушительное использование симбиозов COM-файлов запрещено.
7544 leency 5003
Return to contents.
7496 leency 5004
 
5005
 
7544 leency 5006

7496 leency 5007
  12.6 SYS - драйверы устройств.
7544 leency 5008
7496 leency 5009
 
5010
      Компилятор значительно облегчит Ваш труд при написании драйверов.
5011
  Компилятор сам создаст заголовок драйвера и процедуры СТРАТЕГИЯ и
5012
  ПРЕРЫВАНИЕ. Вам остается лишь написать код обработки команд.
5013
 
5014
      Что бы откомпилировать файл драйвера устройства, надо добавить в
5015
  командную строку ключ /SYS. Кроме того, появились новые директивы
5016
  компилятору, которые действуют только с этим ключом. Вот они:
5017
 
5018
      ?sysattribute значение  - эта  директива передает  компилятору
5019
  атрибут создаваемого драйвера. По умолчанию устанавливается значение
5020
  0x2000.
5021
 
5022
      ?sysname <текстовая  строка> -  эта директива  передает компилятору
5023
  имя будущего драйвера. По умолчанию присваивается имя "NO_NAME". Длина
5024
  имени  не более 8 символов.
5025
 
5026
      ?syscommand command_0,command_1, ... command_n; - эта директива
5027
  является обязательной. По этой директиве компилятору передается список имен
5028
  процедур обработки команд драйвера. Имена разделены запятыми. Список должен
5029
  заканчиваться символом точка-с-запятой. Можно передать не более 25 команд.
5030
  Если какая-то команда не имеет кода поддержки, то в список надо записать
5031
  слово NONE.
5032
 
5033
      По умолчанию компилятор для драйвера не создает стек. Драйвер может
5034
  пользоваться системным стеком. Но, говорят, что он имеет маленькую глубину.
5035
  Если Ваши процедуры активно используют стек, и Вы не надеетесь на системный,
5036
  то директивой ?stack <величина> можно заставить драйвер пользоваться своим
5037
  стеком.
5038
 
5039
      Вашим процедурам обработки команд при передаче управления в регистрах
5040
  ES:BX будет передан адрес заголовка запроса. Регистр DS равен CS. При
5041
  возврате управления ваши процедуры должны сохранить регистр DS. В регистре
5042
  AX должен находиться код возврата. Остальные регистры могут быть
5043
  использованы произвольным образом.
5044
 
5045
      Процедуру обработки команды инициализации желательно располагать
5046
  последней (чтобы иметь возможность отдать адресное пространство занимаемое
5047
  этой процедурой операционной системе). Перед этой процедурой, если Вы в
5048
  других процедурах обработки команд используете динамические процедуры,
5049
  обязательно должна быть директива ?setdinproc. Глобальные переменные должны
5050
  быть обязательно проинициализированы.
7544 leency 5051
Return to contents.
7496 leency 5052
 
5053
 
7544 leency 5054

7496 leency 5055
  12.7 Компиляция кода расширителей ROM-BIOS.
7544 leency 5056
7496 leency 5057
 
5058
      Расширители ROM-BIOS (BIOS видеоконтроллеров, сетевых карт...) имеют
5059
  определенную структуру и требования. C-- теперь может облегчить Вам процесс
5060
  создания кода ROM-BIOS. Если запустить компилятор на компиляцию с ключом
5061
  командной строки /ROM, то компилятор создаст сигнатуру (заголовок)
5062
  ROM-BIOS, заполнит оставшееся свободное место до указанного размера ПЗУ
5063
  кодом заполнения, подсчитает и скорректирует контрольную сумму ПЗУ.
5064
 
5065
      Для этого режима компиляции есть несколько специфических директив:
5066
 
5067
    1.  ?sizerom value - эта директива сообщает компилятору размер ПЗУ в
5068
  байтах. Если эта директива не указана, то компилятор сам выберет
5069
  минимальный подходящий размер ПЗУ из ряда: 1024, 2048, 4096, 8192, 16384,
5070
  32762 или 65536. Свободное от кода и данных место будут заполнены до конца
5071
  размера ПЗУ байтом заполнения определяемого директивой ?aligner. По
5072
  умолчанию он равен нулю, для РПЗУ типа 27ххх этот байт имеет смысл сделать
5073
  равным 0xFF. Последний байт ПЗУ будет скорректирован компилятором таким
5074
  образом, чтобы контрольная сумма равнялась нулю.
5075
 
5076
    2.  ?movedatarom TRUE/FALSE - эта директива сообщает компилятору есть ли
5077
  необходимость копировать данные из ПЗУ в ОЗУ. По умолчанию она установлена
5078
  в FALSE. Если эту директиву определить TRUE, то компилятор вставит в
5079
  область инициализации код перемещающий данные из ПЗУ в ОЗУ. При этом
5080
  регистр DS будет установлен на сегмент ОЗУ. Стек также будет переустановлен
5081
  на этот сегмент. Таким образом, процедура main получит управление с
5082
  регистрами AX = ES = DS = SS = сегменту ОЗУ с перенесенными в него данными.
5083
  Если эту директиву установить в FALSE, регистр DS все равно будет
5084
  переустановлен на адрес сегмента ОЗУ, так как Ваш код будет использовать
5085
  этот сегмент для неинициализированных глобальных переменных.
5086
  Инициализированные переменные останутся в ПЗУ и все обращения к ним будут
5087
  производиться через регистр CS. Так же останется не тронутым (таким, каким
5088
  его установил главный BIOS) и стек.
5089
 
5090
    3.  ?dataseg value - этой директивой компилятору сообщается сегментный
5091
  адрес ОЗУ, который может быть использован вашим кодом. По умолчанию он
5092
  равен 0x70. Этот адрес вы можете узнать в любой момент, считав его из вашего
5093
  кода по смещению 4. Например: DS = CSWORD[4];
5094
 
5095
      Некоторые замечания:
5096
 
5097
    1.  Не забывайте, что в момент инициализации ROM-BIOS, DOS еще не
5098
  загружен, и соответственно все процедуры использующие вызовы DOS работать
5099
  не будут.
5100
 
5101
    2. Нельзя завершать работу программы процедурами ABORT() или EXIT() и им
5102
  подобным. Работа расширителя ROM-BIOS должна завершаться только выходом из
5103
  процедуры main().
5104
 
5105
    3. Если директива ?movedatarom установлена в FALSE, то будьте внимательны
5106
  при работе с инициализированными переменными. Они в этом режиме доступны
5107
  только для чтения, и адресуются через регистр CS.
7544 leency 5108
Return to contents.
7496 leency 5109
 
5110
 
7544 leency 5111

7496 leency 5112
  12.8 32-битные файлы.
5113
 
5114
    12.8.1 32-битный код под DOS.
7544 leency 5115
7496 leency 5116
 
5117
        Для того чтобы откомпилировать 32-битную программу под DOS надо
5118
    запустить компилятор с ключом командной строки /d32. Но работа 32-битной
5119
    программы под DOS-ом невозможна без расширителя DOS. Для C-- можно
5120
    использовать DOS4GW или zrdx.exe или любой другой расширитель DOS. Чтобы
5121
    компилятор знал, где искать stub файл и его имя, надо в файл c--.ini
5122
    прописать строку stub=path_name_to_stub_file.  Пример:
5123
 
5124
        stub=c:\c--\zrdx.exe
5125
 
5126
        Если не добавлять в c--.ini эту строку, то компилятор сгенерирует
5127
    32-битный exe-файл, но без расширителя DOS. Если в командной строке
5128
    вместе с ключом /d32 указать и ключ /ns, то строка с переменной stub из
5129
    файла c--.ini будет аннулирована, и вы получите файл без расширителя DOS.
5130
 
5131
        Для 32-битного DOS-файла можно использовать директивы компилятора
5132
    ?parsecommandline TRUE/FALSE или его расширенный вариант ?argc
5133
    TRUE/FALSE.  Реализована и поддержка директивы ?atexit TRUE/FALSE.
5134
 
5135
        Сейчас для 32-битных DOS-файлов используется LE-формат. Так как LE
5136
    формат является стандартным, то теперь можно использовать почти любой
5137
    stub, понимающий этот формат. Файлы LE формата можно сжимать программами
5138
    типа UPX.EXE и ей подобными.
5139
 
5140
        Если Вы используете stub, который затем загружает DOS4GW.EXE, то
5141
    начало Вашей программы должно иметь специальную сигнатуру. Компилятор
5142
    автоматически сформирует ее, если Вы в командной строке или в c--.ini
5143
    файле укажете ключ /DOS4GW. Такой ключ Вам необходимо будет применять,
5144
    если Вы будете использовать в качестве stub 4gs.exe.
5145
 
5146
        Существует также поддержка блока кода использующего для перехода и
5147
    работы в 32-битном режиме возможности DPMI сервиса. Исходный текст этого
5148
    блока находится в файле startup.h-- и компилируется, если в командной
5149
    строке указана опция /stub=dpmi или в файле c--.ini написать строку
5150
    stub=dpmi.  Недостатком этого способа перехода и работы в 32-битном
5151
    режиме являются необходимость обязательного функционирования на
5152
    запускаемом компьютере DPMI сервиса. Так как, программа загружается как
5153
    обычная DOS программа, и лишь в процессе работы переходит в 32-битный
5154
    режим работы, размер программы ограничен размером свободной DOS памяти.
5155
    Ну а преимуществом его является компактный размер исполняемого файла.
7544 leency 5156
Return to contents.
7496 leency 5157
 
5158
 
7544 leency 5159

7496 leency 5160
    12.8.2 32-битный код под Windows.
7544 leency 5161
7496 leency 5162
 
5163
        Для того чтобы откомпилировать программу, написанную под Windows надо
5164
    запустить компилятор с ключом командной строки /w32.
5165
 
5166
        Если Вы в своей программе используете вызовы API-процедур, то эти
5167
    процедуры надо предварительно обязательно объявить. Объявление процедур
5168
    имеет следующую форму:
5169
 
5170
    extern WINAPI "DLL_name"
5171
    {
5172
        returncode procname1();
5173
        returncode procname2();
5174
        procname3();
5175
    }
5176
 
5177
      где:
5178
           DLL_name - имя и расширение dll-библиотеки, в которой находятся эти
5179
                      процедуры.
5180
         returncode - тип возврата из api-процедур. По умолчанию он равен dword.
5181
 
5182
        Программы, написанные под Windows, имеют одну немаловажную
5183
    особенность - все параметры в стековые процедуры передаются в обратном
5184
    порядке (так называемый C-стиль), но очистка стека от параметров
5185
    происходит в самих процедурах. Получается своеобразный гибрид C и pascal
5186
    стилей - stdcall.
5187
 
5188
        С помощю ключа /W32C компилятор создает консольный файл под Windows.
5189
 
7544 leency 5190
        Если при компиляции указывали опцию командной строки /j0 или
7496 leency 5191
    директиву #jumptomain NONE, то Ваша программа будет компилироваться без
5192
    использования кода начальной инициализации, описание которого находится в
5193
    файле startup.h--.
5194
 
7544 leency 5195
        Код начальной инициализации для программ под Windows имеет следующий
7496 leency 5196
    вид:
5197
 
7544 leency 5198
        hThisInst=GetModuleHandleA(0);
7496 leency 5199
      #ifdef __CONSOLE__
7544 leency 5200
        hStdOut=GetStdHandle(-11);
7496 leency 5201
      #endif
5202
        lpszArgs=GetCommandLineA();
5203
      #ifdef __environ;
7544 leency 5204
        environ=GetEnvironmentStringsA();
7496 leency 5205
      #endif
7544 leency 5206
        main();
5207
        ExitProcess(EAX);
7496 leency 5208
 
7544 leency 5209
        Таким образом, в глобальных переменных hThisInst будет находится
7496 leency 5210
    handl запущенного файла, а в lpszArgs адрес командной строки Вашего
5211
    файла. Если Вы в командной строке указали опции /p или /argc или в
5212
    начале вашего файла есть директивы #parsecommandline TRUE или argc TRUE,
7544 leency 5213
        то компилятор создаст дополнительный код сделающий разборку этой
7496 leency 5214
    командной строки на части. Если Вы компилируете консольную программу, то
5215
    в вашей программе будет еще одна глобальная переменная - hStdOut. В этой
5216
    переменной хранится handl стандартного вывода (экрана). Если Вы при
5217
    компиляции программы указали опцию /env, то в глобальной переменной
5218
    environ хранится адрес переменной окружения программы.
5219
 
7544 leency 5220
        После завершения работы процедуры main выполнятся процедура
7496 leency 5221
    ExitProcess, которой в качестве параметра передается регистр EAX. Т.о.
5222
    Вам для завершения работы программы будет достаточно сделать выход из
5223
    процедуры main, предварительно загрузив в регистр EAX нужный Вам код
5224
    возврата.
5225
 
5226
        Некоторые компиляторы создают DLL, в которых имена экспортируемых
5227
    процедур имеют такой формат:
5228
 
5229
       ProcName@8
5230
 
5231
        В этом имени после символа @ указывается размер стека с
5232
    параметрами, передаваемых процедуре.
5233
 
5234
        Объявлять такие процедуры нужно так:
5235
 
5236
    extern WINAPI "name.dll"
5237
    {
5238
       ProcName@8 ;
5239
    }
5240
 
5241
    т.е. без круглых скобок. В программе, при обращении к такой процедуре, ее
5242
    имя надо писать без суффикса @8, т.е. вот так - ProcName(param1,param2);
7544 leency 5243
Return to contents.
7496 leency 5244
 
5245
 
7544 leency 5246

7496 leency 5247
    12.8.3 Вызов API процедур по ординалам.
7544 leency 5248
7496 leency 5249
 
5250
        В динамически подключаемых библиотеках (DLL) каждой процедуре, кроме
5251
    ее имени, соответствует уникальное число, которое называется ординалом. И
5252
    поэтому, кроме общепринятого вызова API-процедуры по имени, можно делать
5253
    вызов и по ординалу. Теоретически, при использовании вызова по ординалу,
5254
    загрузка файла должна происходить быстрее. Так как в выходной файл не
5255
    будут включены списки имен процедур, вызов которых производится по
5256
    ординалам, то выходной файл может получиться немного меньшим по размеру.
5257
 
5258
        Чтобы компилятор создал файл, использующий вызов API-процедур по
5259
    ординалам, надо сделать две вещи:
5260
 
5261
     1. Разрешить компилятору это делать. Для этого надо в опциях командной
5262
    строки (или в файле C--.INI) указать ключ WO.
5263
 
5264
     2. Сообщить компилятору - какой номер ординала соответствует какому
5265
    имени процедуры. Процедуры, для которых не был указан ординал, будет
5266
    создан вызов по имени. Установить соответствие имен процедур ординалу
5267
    можно двумя способами:
5268
 
5269
        a). Автоматически, с помощью опции командной строки IND=name.dll,
7544 leency 5270
        по которой компилятор просканирует эту библиотеку и импортирует из
5271
        нее все имена и ординалы процедур. (Импорт возможет только из
5272
        библиотек имеющих формат PE).
7496 leency 5273
 
5274
        b). В ручную указать в объявлении API-процедур и ее ординал. Делается
7544 leency 5275
        это так: после имени процедуры ставится точка, а за ней указывается
5276
        номер ординала. Вот пример объявления API-процедуры с указанием ее
5277
        ординала:
7496 leency 5278
 
5279
    extern WINAPI "user32.dll"
5280
    {
5281
      ............
5282
      long  MessageBoxA.429();
5283
      ............
5284
    }
5285
 
5286
        В библиотеках (DLL), иногда существуют процедуры, для которых не
5287
    указано их имя, но указан номер ординала. Вызов таких процедур по имени
5288
    не возможен, но можно это сделать по ординалу (если, конечно Вы знаете,
5289
    для чего эта процедура и что она делает). Для этого в объявлении
5290
    API-процедуры Вам надо придумать для этой процедуры уникальное имя и
5291
    указать реальный ординал. Затем в программе Вы будете обращаться к этой
5292
    процедуре по вымышленному имени. Но если Вы случайно откомпилируете такой
5293
    файл без ключа WO, то при запуске этой программы Вы получите сообщение,
5294
    о том, что данного имени в библиотеке нет.
5295
 
5296
        К сожалению, нет никаких гарантий того, что номер ординала для данной
5297
    процедуры не изменится при смене версии динамической библиотеки. Поэтому
5298
    использовать ординалы надо осторожно.
7544 leency 5299
Return to contents.
7496 leency 5300
 
5301
 
7544 leency 5302

7496 leency 5303
    12.8.4 Создание DLL под Windows.
7544 leency 5304
7496 leency 5305
 
5306
        Динамически подключаемые библиотеки позволят получать более
5307
    компактные программы и ускорить процесс компиляции. К минусам
5308
    использования DLL можно отнести необходимость наличия самих файлов DLL на
5309
    запускаемом компьютере и немного увеличивается время запуска программы.
5310
 
5311
        Для того чтобы процедура стала доступной для других программ надо в
5312
    исходном тексте перед именем процедуры прописать ключевое слово - _export.
5313
    Пример:
5314
 
5315
      void _export testproc()
5316
      {
5317
        ....
5318
      }
5319
 
5320
        Для того чтобы создать DLL, нужно написать файл, в котором будут
5321
    процедуры с ключевыми словами _export. Вспомогательные процедуры, которые
5322
    могут понадобиться для работы основных экспортируемых процедур, объявлять
5323
    как _export необязательно. Затем этот файл нужно откомпилировать с ключом
5324
    /dll.  В результате Вы получите готовую динамически подключаемую
5325
    библиотеку.
7544 leency 5326
Return to contents.
7496 leency 5327
 
5328
 
7544 leency 5329

7496 leency 5330
    12.8.5 Инициализация DLL при загрузке.
7544 leency 5331
7496 leency 5332
 
5333
        Иногда, для работы процедур из динамических библиотек (DLL), бывает
5334
    необходимым инициализировать некоторые переменные значениями, зависящими
5335
    от текущего состояния операционной системы, например, получить дескриптор
5336
    этой библиотеки.
5337
 
5338
      Директивой #jumptomain NONE (-j0) управление при запуске передается
5339
    сразу на процедуру main.
5340
 
5341
      Во всех остальных случаях генерируется код заглушки и управление на
5342
    процедуру main не передается. Фактически процедура main в этом случае не
5343
    нужна.
5344
 
5345
      Процедура main при создании файлов DLL должна выглядеть немного иначе,
5346
    чем в других случаях:
5347
 
5348
    dword main ( dword hInstDLL, reason, reserv )
5349
    {
5350
      ...
5351
    }
7544 leency 5352
Return to contents.
7496 leency 5353
 
5354
 
7544 leency 5355

7496 leency 5356
    12.8.6 Компиляция ресурсов.
7544 leency 5357
7496 leency 5358
 
5359
        Встроенный в C-- компилятор ресурсов по своим возможностям уступает
5360
    специализированным компиляторам ресурсов, но этих возможностей, как мне
5361
    кажется, будет достаточно для большинства Ваших задач.
5362
 
5363
        Будет проще перечислить то, что встроенный в C-- компилятор ресурсов
5364
    не умеет делать. Не обрабатываются операторы ресурсов: VERSION,
5365
    VERSIONINFO и определяемые пользователем ресурсы. При необходимости,
5366
    данные, вводимые с помощью этих операторов, можно ввести с помощью
5367
    оператора RCDATA. У многих операторов ресурсов есть необязательные
5368
    параметры loading и 'memory'.  Поддержка этих параметров не
5369
    реализована. Встретив эти параметры, компилятор их просто пропустит.
5370
 
5371
        Заставить компилятор C-- обрабатывать ресурсы можно двумя способами:
5372
 
5373
        1. Включить в свой проект директивой #include файл с расширением
5374
    .rc.  Файлы с таким расширением компилятор считает файлом с ресурсами.
5375
    Файл ресурсов необходимо включать в Ваш проект лишь после включения
5376
    заголовочных файлов Windows.
5377
 
5378
        2. Ресурсы можно располагать в теле исходного текста программы в
5379
    произвольном месте. Текст ресурсов должен начинаться с директивы #pragma
5380
    resource start, а заканчиваться директивой #pragma resoutce end.
5381
    Ресурсы могут быть разделенными на части и эти части можно располагать в
5382
    любом удобном для Вас месте (глупо располагать ресурсы в блоке
5383
    комментариев и потом удивляться, почему они не были откомпилированы).
5384
    Компилятор соберет эти части и откомпилирует.
5385
 
5386
        Имена операторов можно писать как большими, так и маленькими буквами,
5387
    но имена идентификаторов чувствительны к регистру.  В тексте ресурсов
5388
    можно использовать директивы и комментарии.
5389
 
5390
        Ничто не мешает Вам использовать компиляторы ресурсов от других
5391
    языков.  Главное, чтобы синтаксис файла ресурсов соответствовал выбранному
5392
    компилятору.
7544 leency 5393
Return to contents.
7496 leency 5394
 
5395
 
7544 leency 5396

7496 leency 5397
  12.9 Выходные файлы для MeOS.
7544 leency 5398
7496 leency 5399
 
5400
      Исполняемые файлы для операционной системы MenuetOS поддерживаются
5401
  компилятором совсем недавно. Для того, чтобы откомпилировать файл для
5402
  MenuetOS, нужно в опциях компилятору указать /meos. Вы получите файл без
5403
  расширения, который потом можно будет выполнить в среде операционной
5404
  системы MenuetOS.
5405
 
5406
      Если при компиляции файла Вы не указывали опцию /j0 или не
5407
  использовали директиву #jumptomain NONE, то компилятор будет использовать
5408
  файл начальной инициализации startup.h--, в котором для операционной
5409
  системы MenuetOS создан блок инициализации и завершения программы.
5410
  Завершать выполнение таких программ можно просто выйдя из процедуры main.
7544 leency 5411
Return to contents.
7496 leency 5412
 
5413
 
7544 leency 5414

7496 leency 5415
13. Приложения.
5416
 
5417
  13.1 Поиск включаемых файлов.
7544 leency 5418
7496 leency 5419
 
5420
      Поиск включаемого в вашу программу файла, имя которого объявляется
5421
  директивой include и заключено в двойные кавычки "", производится
5422
  компилятором по такой схеме:
5423
 
5424
  сначала делается попытка открыть файл в текущей директории. Если файла там
5425
  нет, то далее делается попытка открыть файл в директории указанной
5426
  директивой #includepath. Если директива не была задана или файла в этой
5427
  директории не оказалось, то делается попытка открыть файл в директории
5428
  указанной в командной строке командой /ip=path. Если эта команда не была
5429
  задана или файла в указанной директории не оказалось, то делается попытка
5430
  открыть файл в директории указанной в файле C--.INI командой ip=. Если эта
5431
  команда не была задана или файла в указанной директории не оказалось, то
5432
  делается попытка открыть файл в директории, на которую указывает переменная
5433
  окружения C--. Если переменная окружения не была задана или файла в этой
5434
  директории не оказалось, то делается последняя попытка открыть файл в
5435
  директории, откуда был запущен компилятор.
5436
 
5437
      Если имя включаемого файла заключено в угловые скобки < >, то поиск
5438
  этого файла производится в противоположном направлении, за исключением
5439
  того, что поиск в текущей директории не производится.
5440
 
5441
      Для консольной версии компилятора имена главного модуля и включаемых
5442
  файлов могут иметь длину более 8 символов.
7544 leency 5443
Return to contents.
7496 leency 5444
 
5445
 
7544 leency 5446

7496 leency 5447
  13.2 Регистры, которые должны быть сохранены.
7544 leency 5448
7496 leency 5449
 
5450
      Регистры, которые должны сохраняться - BP, DI, SI, DS, SS, SP, CS и IP.
5451
 
5452
      BP используется как указатель на локальные и параметрические
5453
  переменные в стеке, что и требует его сохранения.
5454
 
5455
      DI и SI сохранять не обязательно, если программист осознает
5456
  последствия. DI и SI часто используются для индексации массивов, как
5457
  например в формуле:
5458
 
5459
        dog = firehydrant(1,red) + legs[DI];
5460
 
5461
      Если DI не сохранялся в процедуре firehydrant, значение, присвоенное
5462
  переменной dog, скорее всего, будет неправильным, поскольку индекс для
5463
  массива legs был изменен. В сущности, для точного согласования все
5464
  процедуры должны иметь специальное указание в комментарии на то, что в них
5465
  не сохраняется содержимое регистров DI и/или SI.
5466
 
5467
      DS указывает на сегмент данных, и все операции с глобальными
5468
  переменными пользуются этим значением.
5469
 
5470
      SS хранит сегмент стека и должен сохраняться. SP указывает на текущую
5471
  позицию в стеке и тоже должен сохраняться.
5472
 
5473
      CS хранит сегмент кода программы.  Все команды выбираются с
5474
  использованием CS и IP, следовательно их значения должны сохраняться. IP,
5475
  как известно, указатель адреса команды, и CS и IP непосредственно не могут
5476
  изменяться в процессорах 8086, 8088, 80286, 80386, 80486,...
7544 leency 5477
Return to contents.
7496 leency 5478
 
5479
 
7544 leency 5480

7496 leency 5481
  13.3 C--.ini файл.
7544 leency 5482
7496 leency 5483
 
5484
      C--.ini файл предназначен для предустановки по умолчанию параметров
5485
  компилятора.
5486
 
5487
      Сейчас компилятор поддерживает огромное число параметров командной
5488
  строки. Правильное их использование позволит Вам получать более компактный
5489
  код и может значительно облегчить Вам отладку программы. Но так как этих
5490
  параметров очень много набирать их каждый раз в командной строке бывает
5491
  утомительно и не исключена возможность пропустить какой-нибудь параметр.
5492
  Чтобы избавить Вас от всех этих напастей и был введен c--.ini файл.
5493
 
5494
       Параметры командной строки прописываются в этом файле построчно.
5495
  Синтаксис тот же, что и в командной строке, но без ведущего обратного слэша
5496
  или минуса. Если файл расположен в директории, на которую указывает
5497
  переменная окружения set c--= или если эта переменная не определена,
5498
  то в той же директории где и файл c--.exe, то эти параметры
5499
  распространяются на все компилируемые программы. Если же файл c--.ini
5500
  расположен в текущей директории, то параметры считываются только из этого
5501
  файла и действуют только для текущего проекта.
5502
 
5503
      Допустимо использование комментариев. Признаком начала комментария
5504
  является символ ;. Все последующие символы после ; и до конца строки
5505
  считаются комментарием.
5506
 
5507
      Пример C--.ini файла:
5508
 
5509
  r-
5510
  X
5511
  3     ;это комментарий
5512
  os
5513
 
5514
      ini-файл может иметь любое имя (но расширение должно быть обязательно
5515
  ini). Имя этого файла с расширением должно быть передано компилятору в
5516
  командной строке. Файл c--.ini загружается и обрабатывается автоматически
5517
  до загрузки файла указанного в командной строке.
5518
 
5519
      Таким образом, файл *.ini можно использовать подобно make-файлу - в нем
5520
  Вы можете указать и имя главного компилируемого модуля, и все необходимые
5521
  для его компиляции настройки.
5522
 
5523
      Как альтернативу c--.ini файлу, параметры командной строки можно
5524
  прописывать непосредственно в начале главного файла компилируемого проекта,
5525
  используя директиву pragma option. С одной стороны это обеспечит Вашему
5526
  проекту независимость от настроек компилятора, если Ваш проект будет
5527
  компилироваться на другом компьютере. Но с другой стороны некоторые
5528
  настройки являются индивидуальными для данного компьютера (это расположение
5529
  библиотек, имена и расположение stub-файлов). Какой вариант использовать
5530
  решать Вам, но как говорят, и я с этим согласен, лучше пользоваться золотой
5531
  серединой - Часть параметров прописать в c--.ini файле, а другую
5532
  непосредственно в компилируемом файле.
7544 leency 5533
Return to contents.
7496 leency 5534
 
5535
 
7544 leency 5536

7496 leency 5537
  13.4 startup.h-- файл.
7544 leency 5538
7496 leency 5539
 
5540
      В этом файле находятся исходные тексты, которые компилируются
5541
  компилятором в код начальной инициализации файла, для всех поддерживаемых
5542
  компилятором типов выходных файлов. Этот файл должен находится либо в
5543
  директории вместе с компилятором, либо в директории с библиотечными файлами.
5544
  Этот файл включается компилятором в проект автоматически, а включение его
5545
  директивой include может привести к нежелательным результатам.
5546
 
5547
      В блоке начальной инициализации программы может производится (если Вы
5548
  это укажете с помощью опций командной строки или используя директивы),
5549
  разбор командной строки на параметры, сохранение переменой окружения,
5550
  поддержка работы процедуры ATEXIT, изменение размера доступной памяти для
5551
  *.com файлов и многие другие подготовительные операции. Если Вы
5552
  откомпилируете свой файл не используя никаких опций командной строки и у
5553
  Вас будет отсутствовать c--.ini файл, а в самом компилируемом файле у Вас
5554
  будут отсутствовать директивы, то при компиляции *.com файла в него будет
5555
  включен блок изменяющий размер доступной памяти и сигнатура SPHINXC--.
5556
 
5557
      Если Вы компилируете файл типа *.exe (кроме файла модели tiny для DOS)
5558
  и используете директиву jumptomain NONE или ключ командной строки /j0,
5559
  то для этого проекта файл startup.h-- компилятором не используется. Не
5560
  используется этот файл также при компиляции *.com файлов если, кроме /j0,
5561
  в этом проекте не используется разбор командной строки (/p /argc), не
5562
  применяется процедура ATEXIT (/at), не используется адрес переменной
5563
  окружения (/env), не используется очистка области post-адресов (/cpa), не
5564
  используется уменьшение доступной программе памяти (/r) и не используется
5565
  заглушка нажатий CTRL-C (/c).
5566
 
5567
      Кроме блока начальной инициализации программы в файле startup.h--
5568
  находятся динамические процедуры:
5569
 
5570
  void CLEARPOSTAREA( (E)AX );  - очистка post-области данных.
5571
  unsigned int PARAMSTR( ECX ); - получить адрес элемента командной строки
5572
  unsigned int PARAMCOUNT();    - получить число элементов в командной строке
5573
 
5574
      При разборе командной строки на составляющие ее элементы для 32-битных
5575
  программ реализована поддержка длинных имен. Для 16-битных программ
5576
  поддержка разбора командной строки с учетом длинных имен подключается, если
5577
  Вы в начале свой программы укажете директиву:
5578
 
5579
  #define _USELONGNAME TRUE
5580
 
5581
      либо в c--.ini файле или в командной строке компилятора укажете опцию
5582
  d=_USELONGNAME.
7544 leency 5583
Return to contents.
7496 leency 5584
 
5585
 
7544 leency 5586

7496 leency 5587
  13.5 mainlib.ldp файл.
7544 leency 5588
7496 leency 5589
 
5590
      В этом файле находится большое число процедур из основной библиотеки
5591
  компилятора в уже откомпилированном виде. Все процедуры откомпилированы в
5592
  4-х различных режимах оптимизации. В этот файл также вынесены многие
5593
  процедуры, которые ранее были внутри компилятора. Использование ранее
5594
  откомпилированных процедур повышает скорость компиляции.
5595
 
5596
      Эти процедуры откомпилированы только для 16-битного режима работы
5597
  программы. Если Вы будете использовать эти процедуры в 32-битной программе,
5598
  то компилятор на это не выдаст никаких сообщений и включит эту процедуру в
5599
  Ваш код. Но при запуске такой программы она неизбежно потерпит крах.
5600
 
5601
      Использовать эту библиотеку очень просто. Все что нужно, это
5602
  расположить эту библиотеку в одной с компилятором директории. Тогда
5603
  компилятор, если встретит в вашей программе вызов процедуры, которая не
5604
  была определена ни во включаемых в программу библиотечных файлах, ни в
5605
  вашей программе, будет искать эту процедуру в файле mainlib.ldp. Если эта
5606
  процедура будет найдена в этом файле, то ее код будет перенесен в Ваш файл,
5607
  иначе будет выдано сообщение о неизвестной процедуре. Таким образом, чтобы
5608
  процедура была вставлена в вашу программу из библиотеки mainlib.ldp Вам
5609
  нужно в свою программу не включать библиотечный файл, содержащий процедуру с
5610
  таким же именем.
5611
 
5612
      Список процедур находящихся в этой библиотеке можно получить с помощью
5613
  специальной программы cmmlib.exe. Эту программу можно найти в архиве
5614
  cmmlib.rar. Извлеките программу cmmlib.exe из этого архива и расположите ее
5615
  в одной с компилятором директории. Затем запустите эту программу с ключом
5616
  /L и Вы получите список процедур находящихся в этой библиотеке.
7544 leency 5617
Return to contents.
7496 leency 5618
 
5619
 
7544 leency 5620

7496 leency 5621
  13.6 C-- символы.
7544 leency 5622
7496 leency 5623
 
5624
  SYMBOL|FUNCTION                  |EXAMPLE
5625
  --------------------------------------------------------------------
5626
    /*  |начинают блок комментария |/* комментарий */
5627
    */  |завершают блок комментария|/* комментарий */
5628
        |                          |
5629
    //  |комментарий до конца линии|// комментарий
5630
        |                          |
5631
     =  |присвоение                |AX = 12;
5632
     +  |сложение                  |AX = BX + 12;
5633
     -  |вычитание                 |house = dog - church;
5634
     *  |умножение или указатель   |x = y * z; AL = * var;
5635
     /  |деление                   |x1 = dog / legs;
5636
     &  |поразрядное логическое И  |polution = stupid & pointless;
5637
     |  |поразрядное логическое ИЛИ|yes = i | mabe;
5638
     ^  |поразрядн. исключающее ИЛИ|snap = got ^ power;
5639
    <<  |битовый сдвиг влево       |x = y << z;
5640
    >>  |битовый сдвиг вправо      |x = y >> z;
5641
        |                          |
5642
    +=  |сложение                  |fox += 12;   // fox = fox +12;
5643
    -=  |вычитание                 |cow -= BX;   // cow = cow - BX;
5644
    *=  |умножение                 |a *= b;      // a = a * b;
5645
    /=  |деление                   |a /= b;      // a = a / b;
5646
    &=  |поразрядное логическое И  |p &= q;      // p = p & q;
5647
    |=  |поразрядное логическое ИЛИ|p |= z;      // p = p | z;
5648
    ^=  |поразрядн. исключающее ИЛИ|u ^= s;      // u = u ^ s;
5649
    <<= |битовый сдвиг влево       |x <<= z;     // x = x << z
5650
    >>= |битовый сдвиг вправо      |x >>= z;     // x = x >> z
5651
        |                          |
5652
    ><  |обмен значениями          |x >< y; /* меняет местами значения x и y */
5653
        |                          |
5654
    ==  |проверка на равенство     |IF(AX == 12)
5655
     >  |проверка на больше чем    |IF(junk > BOGUS)
5656
     <  |проверка на меньше чем    |if( x < y )
5657
    >=  |проверка больше или равно |if(AX >= 12)
5658
    <=  |проверка меньше или равно |IF(BL >= CH)
5659
   !=   |проверка на неравенство   |IF(girl != boy)
5660
    <>  |проверка на отличие       |if (cat<>dog) /* та же функция что != */
5661
        |                          |
5662
    @   |вставка кода              |@ COLDBOOT(); /* вставляет COLDBOOT код */
5663
    :   |динамическая процедура    |: functionname () //объявляет functionname
5664
    $   |ассемблерная команда      |$ PUSH AX   /* заносит AX в стек */
5665
    #   |получение адреса(смещения)|loc = #cow;    /* loc = address of cow */
5666
        |или директива             | #resize FALSE
5667
    !   |оператор NOT или смена    |!x_var;  if(!proc())
5668
        |флага операции сравнения. |
5669
   ...  |любое число параметров в  | void proc(...);
5670
   ::   |разрешение видимости      | ::var=0;
7544 leency 5671
Return to contents.
5672
 
5673
7496 leency 5674
7544 leency 5675


7496 leency 5676