Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
7496 leency 1
2
Документация на C--
3
4
5
  
6
    
7
    
8
    
9
      
10
        
11
          
12
            
13
            
14
            
15
            
16
              
17
              Документация на C--.
18
            
19
            
20
            
21
            
22
            
23
24
 
25
26

27
Содержание.
28

29
                
30
 
31
32
1      Введение.
33
1.1    История создания и развития.
34
1.2    Что такое C--?
35
1.3    Как установить C--.
36
37
2.     Управление компиляцией.
38
2.1    Параметры командной строки компилятора C--.
39
2.1.1  /ON - Оптимизация числовых выражений.
40
2.1.2  /DE - Временное расширение разрядности переменной.
41
2.1.3  /ARGC - Альтернативный обработчик командной строки.
42
2.1.4  /OST - слияние одинаковых строковых констант.
43
2.1.5  /D - установка идентификатора в TRUE из командной строки.
44
2.1.6  /IA - упрощенный ввод ассемблерных инструкций.
45
2.1.7  /CRI - пропуск повторно включаемого файла.
46
2.1.8  /IND - импорт имен процедур из DLL.
47
2.1.9  /WS - задать имя stub файла для программ под windows.
48
2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции.
49
2.1.11 /DBG - создание отладочной информации.
50
2.1.12 /J0 /J1 /J2.
51
2.1.13 /LST - Создание ассемблерного листинга.
52
2.1.14 /ENV - Сохранение адреса переменных окружения.
53
2.1.15 /CPA - Очистка post-области данных.
54
2.1.16 /W - вывод предупреждений.
55
2.1.17 /NW - Выборочное отключение типов предупреждений.
56
2.1.18 /WSI - короткая таблица импорта.
57
2.2    Директивы транслятора.
58
2.2.1  ?ifdef/?ifndef
59
2.2.2  ?initallvar
60
2.2.3  ?usestartup
61
2.2.4  ?startusevar
62
2.2.5  ?atexit
63
2.2.6  ?startuptomain
64
2.2.7  ?undef
65
2.2.8  ?align и ?aligncode
66
2.2.9  ?pragma
67
68
3.     Константы.
69
3.1    Числовые константы.
70
3.2    Символьные константы.
71
3.3    Строковые константы.
72
3.4    Постоянные выражения.
73
74
4.     Выражения.
75
4.1    Типы выражений.
76
4.2    Выражения типа EAX/AX/AL.
77
4.3    Выражения использующие получатель при вычислении выражения.
78
4.4    Не - EAX/AX/AL выражения.
79
4.5    Условные выражения.
80
4.5.1  Простые условные выражения.
81
4.5.2  Сложные условные выражения.
82
4.6    Изменение типа выражения при присваивании.
83
4.7    Вычисление в регистры EAX/AX/AL со знаком.
84
85
5.     Идентификаторы.
86
5.1    Формат идентификатора.
87
5.2    Зарезервированные идентификаторы.
88
5.3    Универсальные регистры для 16 и 32-битного режима.
89
5.4    Предопределенные идентификаторы.
90
91
6.     Переменные.
92
6.1    Типы переменных.
93
6.2    Объявление переменных.
94
6.3    Глобальные переменные.
95
6.4    Локальные переменные.
96
6.5    Динамические переменные и структуры.
97
6.6    Присваивание одного значения нескольким переменным.
98
6.7    Переменные типа float.
99
6.7.1  Формат переменных типа float.
100
6.7.2  Константы с плавающей точкой.
101
6.7.3  Диапазон допустимых значений.
102
6.7.4  Математические операции.
103
6.7.5  Преобразования типов.
104
6.7.6  Операции сравнения.
105
6.7.7  Сравнение переменных типа float с 32-битным регистром.
106
6.8    Указатели.
107
108
7.     Адресация.
109
7.1    Относительная адресация.
110
7.2    Абсолютная адресация.
111
112
8.     Работа с блоками данных.
113
8.1    Структуры.
114
8.1.1  Что такое структуры.
115
8.1.2  Синтаксис.
116
8.1.3  Инициализация структур при объявлении.
117
8.1.4  Инициализация структуры при выполнении программы.
118
8.1.5  Операции с элементами структур.
119
8.1.6  Вложенные структуры.
120
8.1.7  Отображение тега структуры на блок памяти.
121
8.1.8  Битовые поля структур.
122
8.2    Объединения.
123
8.3    Команды FROM и EXTRACT.
124
125
9.     Операторы.
126
9.1    Условные инструкции.
127
9.2    Циклы do{} while.
128
9.3    Циклы loop, LOOPNZ, loopnz.
129
9.4    Цикл while, WHILE.
130
9.5    Цикл for, FOR.
131
9.6    Оператор переключатель switch.
132
9.7    Оператор перехода goto, GOTO.
133
9.8    Оператор разрыва break, BREAK.
134
9.9    Оператор продолжения continue, CONTINUE.
135
9.10   Логическое объединение условий.
136
9.11   Переход через циклы.
137
9.12   Инвертирование флага проверки условий.
138
9.13   Вычисление выражения, а затем проверка условия.
139
9.14   Проверка битов при операции сравнения.
140
9.15   Оператор перестановки.
141
9.16   Оператор отрицания.
142
9.17   Оператор инверсии.
143
9.18   Специальные условные выражения.
144
9.19   Символ $ - вставляет текущий адрес программы.
145
9.20   Ключевое слово static и оператор ::.
146
9.21   Оператор sizeof.
147
9.22   Метки перехода.
148
149
10.    Ассемблер.
150
10.1   Поддержка команд ассемблера.
151
10.2   Ключевое слово asm.
152
10.3   Префикс dup - повторение инструкций DB/DW/DD.
153
10.4   Инструкции процессора Pentium III.
154
155
11.    Процедуры.
156
11.1   Типы процедур, функций и макрокоманд.
157
11.2   Стековые процедуры.
158
11.3   Регистровые процедуры.
159
11.4   Динамические процедуры.
160
11.4.1 Установка динамической процедуры в определенное место программы.
161
11.5   inline-процедуры.
162
11.5.1 Другое применение inline.
163
11.6   Процедуры обработки прерываний.
164
11.7   Замена return на goto.
165
11.8   Возвращаемые значения.
166
11.9   Объявление параметров в регистровых процедурах.
167
11.10  Объявление параметров в стековых процедурах.
168
11.11  Использование макрокоманд.
169
11.12  Передача параметров в стековые процедуры через регистры.
170
11.13  Вызов процедур с адресом в регистре.
171
11.14  Встоенные в компилятор процедуры.
172
11.14.1 Процедуры ABORT, ATEXIT и EXIT.
173
11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и
174
        outportd.
175
11.14.3 Процедуры для работы с вещественными числами.
176
11.15  Классы.
177
11.15.1 Объявление процедур в структурах.
178
11.15.2 Наследование.
179
11.15.3 Наследование процедур.
180
181
12.    Типы выходных файлов.
182
12.1   Выходные файлы типа COM.
183
12.2   Выходные файлы типа EXE.
184
12.3   Выходной файл *.EXE с моделью памяти tiny.
185
12.4   Объектный выходной файл OBJ.
186
12.5   COM файл symbiosis.
187
12.5.1 СИМБИОЗ - что это такое?
188
12.5.2 Как это делать.
189
12.5.3 Использование.
190
12.5.4 Злоупотребления.
191
12.6   SYS - драйверы устройств.
192
12.7   Компиляция кода расширителей ROM-BIOS.
193
12.8   32-битные файлы.
194
12.8.1 32-битный код под DOS.
195
12.8.2 32-битный код под Windows.
196
12.8.3 Вызов API процедур по ординалам.
197
12.8.4 Создание DLL под Windows.
198
12.8.5 Инициализация DLL при загрузке.
199
12.8.6 Компиляция ресурсов.
200
12.9   Выходные файлы для MeOS.
201
202
13.    Приложения.
203
13.1   Поиск включаемых файлов.
204
13.2   Регистры, которые должны быть сохранены.
205
13.3   C--.ini файл.
206
13.4   startup.h-- файл.
207
13.5   mainlib.ldp файл.
208
13.6   C-- символы.
209
210
 
211
 
212
1. Вступление.
213
 
214
  1.1 История создания и развития.
215
216
 
217
      Автором языка SPHINX C-- является Peter Cellik (CANADA). Последняя
218
  авторская версия SPHINX C-- v0.203 от 28.Oct.96. К сожалению автор
219
  отказался от дальнейшего развития языка. С 1998 года, уже почти умерший
220
  проект, подхватил Михаил Шекер (Россия). Изначально компилятор был freeware
221
  (и даже greenware, как его называл Peter Cellik). Таким статус компилятора
222
  остался и поныне.
223
 
224
      Первоначально компилятор мог создавать только *.com файлы и был
225
  рассчитан на создание небольших demo-программ и резидентов (TSR). В
226
  дальнейшем возможности компилятора расширялись, так как этого требовало
227
  наше бурное время.
228
 
229
      При развитии компилятора, было стремление придерживаться следующих
230
  принципов:
231
 
232
      1. Максимально возможная совместимость синтаксиса с последней версией
233
  компилятора написанного Peter Cellik. Это давало возможность с минимальными
234
  затратами (а чаще всего без всяких затрат) адаптировать программы,
235
  написанные для 0.203 версии компилятора, к последней на этот момент версии
236
  компилятора.
237
 
238
      2. Сблизить синтаксис компилятора со стандартным языком C. Это могло
239
  значительно облегчить перенос программ написанных на C.
240
 
241
      3. Также прилагались усилия, для того, чтобы человек знающий только
242
  ассемблер мог бы с минимальными затратами освоить C--.
243
 
244
      Вот эти, зачастую противоречащие друг другу принципы, влияли на выбор
245
  реализации возможностей компилятора. Насколько это удалось - судить Вам.
246
 
247
      Если у Вас есть предложения и идеи по улучшению компилятора - пишите.
248
  Мой e-mail sheker@mail.ru . Я с удовольствием выслушаю Ваши предложения, но
249
  не гарантирую, что все они будут реализованы. Если реализовывать все
250
  поступающие предложения, то компилятор превратится в свалку. Но если Ваше
251
  предложение будет ценным (на мой взгляд, так что Вам придется свое
252
  предложение хорошо аргументировать) и его будет возможным реализовать, оно
253
  без сомнения найдет место в компиляторе.
254
Return to contents.
255
 
256
 
257
258
  1.2 Что такое C--?
259
260
 
261
      C-- был разработан, для того чтобы строить маленькие и быстрые
262
  программы. Это наиболее подходит для создания резидентных программ (TSR),
263
  программ, требующих обработку прерываний или программ у которых ограничены
264
  ресурсы.
265
 
266
      C-- занимает промежуточное положение между си и ассемблером. В связи с
267
  этим промежуточным положением, Вам, для того чтобы писать программы на C--,
268
  необходимо знать и ассемблер и си. Если Вам надоело возиться с огромными
269
  ассемблерными листингами, а излишняя строгость языка C Вас угнетает, то этот
270
  язык для ВАС.
271
 
272
      Сейчас компилятор C-- может создавать 32-битные программы под Windows
273
  (EXE-файлы формата PE) и 32-битные программы под DOS (LE-формат). Имеет
274
  встроенный компилятор ресурсов и дизассемблер для генерации листинга
275
  откомпилированного файла. Поддерживает ассемблерные инструкции процессора
276
  Pentium III и ассемблерные инструкции FPU. Компилятор может генерировать
277
  отладочную информацию совместимую с отладчиками фирмы Borland. Компилятор
278
  может создавать объектные файлы (obj), но только для DOS программ.
279
 
280
      C-- разработан только для использования на компьютерах с процессорами
281
  совместимыми с семейством 80x86. Компилятор может работать только с
282
  операционными системами DOS и семейством Windows.
283
Return to contents.
284
 
285
 
286
287
  1.3 Как установить C--.
288
289
 
290
      Компилятору C-- для работы нужны совсем незначительные ресурсы:
291
  процессор 386 или лучше, чуть более 1 Мб дискового пространства и 4Мб
292
  оперативной памяти. Компилятор может быть установлен на компьютеры с
293
  операционной системой Windows 95 или лучше. Компилятор также может работать
294
  в среде чистого DOS. В основном пакете компилятора находится 32-битная DOS
295
  версия компилятора. На сайте http://sheker.chat.ru или
296
  http://c--sphinx.narod.ru можно найти и консольную версию компилятора.
297
  Консольная версия компилятора может работать только в среде Windows, но
298
  она, в отличие от DOS версии, может работать с длинными именами исходных
299
  файлов.
300
 
301
      Установить компилятор C-- на Ваш компьютер очень просто. Предположим,
302
  что Вы решили установить C-- на диск C. Создайте на диске C директорию
303
  (папку) с именем C-- или с другим, удобным и понятным для Вас именем
304
  (например, ДОСовской командой: MD C-- или другим доступным Вам способом).
305
  Затем с сайта http://sheker.chat.ru или http://c--sphinx.narod.ru скачайте
306
  файлы full_c--.zip и ful_c--2.zip и разархивируйте их в этой директории.
307
  Затем в файле autoexec.bat можно прописать путь к директории с
308
  компилятором. И все. Компилятор готов к работе. Если Вы добавляли путь к
309
  компилятору в файл autoexec.bat, то Вам придется перегрузить операционную
310
  систему.
311
 
312
      Переменная окружения для компилятора C-- задается либо из командной
313
  строки либо из командного файла (лучше всего ее прописать в autoexec.bat).
314
  Эта переменная должна указывать компилятору, где находятся его библиотечные
315
  файлы. Пример:
316
 
317
    set C--=c:\c--\lib
318
 
319
  Большой необходимости в переменной окружения для сегодняшней версии
320
  компилятора нет. Существует несколько других способов, указать компилятору
321
  место расположения библиотек. Поэтому определять или не определять
322
  переменную окружения дело вашего вкуса и привычек.
323
Return to contents.
324
 
325
 
326
327
2. Управление компиляцией.
328
 
329
  2.1 Параметры командной строки компилятора C--.
330
331
 
332
      Формат командной строки вызова компилятора C--:
333
 
334
  C-- [Параметры] [ИМЯ INI ФАЙЛА] [ИМЯ ИСХОДНОГО ФАЙЛА]
335
 
336
      Имя исходного файла можно задавать без расширения. Компилятор ищет
337
  файл с расширением c--, cmm, c.
338
 
339
      Параметры выделяются предшествующим символом / или -.
340
  Инвертировать функцию опции можно завершающим символом -.
341
 
342
  Список поддерживаемых параметров:
343
 
344
  /0          использовать только команды 8086/8088 процессора (установлено
345
              по умолчанию при компиляции 16-битных программ).
346
  /1          использовать команды 80186 процессора.
347
  /2          использовать команды и оптимизацию для 80286 процессора.
348
  /3          использовать команды и оптимизацию для 80386 процессора.
349
              (установлено по умолчанию для 32-битных программ).
350
  /4          использовать команды и оптимизацию для 80486 процессора.
351
  /5          использовать команды и оптимизацию для Pentium процессора.
352
  /6          использовать команды и оптимизацию для Pentium MMX процессора.
353
  /7          использовать команды и оптимизацию для Pentium Pro процессора.
354
  /8          использовать команды и оптимизацию для Pentium II процессора.
355
  /9          использовать команды и оптимизацию для Pentium III процессора
356
              (пока не реализовано из-за отсутствии информации).
357
  /A          выравнивание данных на четный адрес
358
              по умолчанию разрешено, поддерживает инверсию
359
  /AC         выравнивание адреса начала циклов
360
              по умолчанию отключено, поддерживает инверсию
361
              имеет смысл только на процессорах Pentium+
362
  /AL=##      установить значение байта заполнения при выравнивании данных
363
              по умолчанию 0.
364
  /AP         выравнивание адреса начала процедур.
365
              по умолчанию отключено, поддерживает инверсию
366
              имеет смысл только на процессорах Pentium и лучше
367
  /ARGC       вставить блок разбора командной строки
368
              по умолчанию отключено, поддерживает инверсию
369
  /AS         выравнивание в структурах.
370
              по умолчанию отключено, поддерживает инверсию
371
  /AT         вставить блок поддержки ATEXIT процедуры
372
              по умолчанию отключено, поддерживает инверсию
373
  /C          вставить блок игнорирования CTRL-C
374
              по умолчанию отключен, поддерживает инверсию
375
              имеет смысл только под DOS программы
376
  /CRI        проверять включаемые файлы на повторную загрузку
377
              по умолчанию включено, поддерживает инверсию
378
  /CPA        очистка post-области данных
379
  /D32        создать EXE файл (32 битный код под DOS)
380
              по умолчанию COM
381
  /D=idname   определить идентификатор для условной компиляции
382
              по умолчанию нет
383
  /DBG        генерировать отладочную информацию
384
              по умолчанию нет
385
  /DE         временное расширение разрядности после умножения
386
              по умолчанию отключено, поддерживает инверсию
387
  /DLL        создать DLL для Windows32
388
              по умолчанию COM
389
  /ENV        сохранение адреса переменных окружения
390
  /EXE        создать EXE файл для DOS (модель SMALL)
391
              по умолчанию COM
392
  /HELP /H /? справка, эта информация
393
  /IA         имена ассемблерных инструкций являются идентификаторами
394
              по умолчанию отключено, поддерживает инверсию
395
  /IND=name   импорт имен из файла name.
396
  /IP=path    задать путь поиска включаемых файлов
397
              по умолчанию нет
398
  /IV         инициализировать все переменные
399
              по умолчанию отключено, поддерживает инверсию
400
  /J0         не делать начальный jump на main()
401
              по умолчанию отключено, поддерживает инверсию
402
              В COM-файлах не создает jmp на main. В остальных не создается
403
              блок начальной инициализации программы, а управление
404
              передается сразу на main.
405
  /J1         делать короткий jump на main()
406
              по умолчанию нет
407
              имеет смысл только в COM-файлах
408
  /J2         делать jump на main()
409
              по умолчанию да, поддерживает инверсию
410
              имеет смысл только в COM-файлах
411
  /LAI        список поддерживаемых ассемблерных инструкций
412
  /LRS        загружать числовые константы через стек.
413
              по умолчанию да, поддерживает инверсию
414
  /LST        создать ассемблерный листинг
415
  /ME         показать мой адрес и имя
416
  /MEOS       создать исполняемый файл для MeOS
417
              по умолчанию COM
418
  /MER=##     установить максимальное число ошибок
419
              по умолчанию 16
420
  /MIF=file   определить имя главного компилируемого файла
421
  /NS         запретить подключать stub файлов
422
              по умолчанию нет, поддерживает инверсию
423
  /NW=##      выборочное отключение предупреждений
424
  /OBJ        создать OBJ файл
425
              только 16 битный код.
426
              по умолчанию COM
427
  /OC         оптимизировать по размеру кода
428
              по умолчанию нет, поддерживает инверсию
429
  /ON         оптимизация чисел
430
              по умолчанию нет, поддерживает инверсию
431
  /OS         оптимизация по скорости выполнения
432
              по умолчанию да, поддерживает инверсию
433
  /OST        оптимизация строковых идентификаторов
434
              по умолчанию отключено, поддерживает инверсию
435
  /P          вставить блок разборки командной строки
436
              по умолчанию нет, поддерживает инверсию
437
  /R          вставить блок уменьшающий размер доступной памяти.
438
              по умолчанию да, поддерживает инверсию
439
              имеет смысл только в DOS-файлах
440
  /S=#####    установить размер стека
441
              по умолчанию 2048
442
  /SA=####    начальное смещение адреса запуска программы
443
              имеет смысл только в COM-файлах, по умолчанию 0x100
444
  /SOBJ       создать ведомый OBJ файл
445
              по умолчанию COM
446
  /STM        перенести блок startup кода в процедуру main
447
              по умолчанию нет, поддерживает инверсию
448
              имеет смысл только в COM-файлах
449
  /SUV=####   начальный адрес не инициализированных переменных, при
450
              использовании ими startup кода.
451
              имеет смысл только в COM-файлах, по умолчанию равен /SA
452
  /SYM        надстройка для COM файла
453
              по умолчанию COM
454
  /SYS        создать драйвер устройств (SYS)
455
              по умолчанию COM
456
  /TEXE       создать EXE файл для DOS (модель TINY)
457
              по умолчанию COM
458
  /UL         использовать lea при оптимизации сложения регистров.
459
              по умолчанию да, поддерживает инверсию
460
  /UST        использовать startup код для переменных.
461
              имеет смысл только в COM-файлах
462
              по умолчанию нет, поддерживает инверсию
463
  /W          разрешить предупреждения
464
              по умолчанию нет, поддерживает инверсию
465
  /W32        создать EXE файл для Windows32 GUI
466
              по умолчанию COM
467
  /W32C       создать EXE файл для Windows32 console
468
              по умолчанию COM
469
  /WBSS       помещать не инициализированные данные в отдельную секцию.
470
              по умолчанию для /w32 разрешено, для остальных запрещено.
471
              поддерживает инверсию
472
  /WF=file    перенаправить вывод предупреждений в файл.
473
              по умолчанию нет
474
  /WFA        использовать быстрые вызовы API процедур
475
              по умолчанию да, поддерживает инверсию
476
              только под windows
477
  /WFU        создавать таблицу перемещений (для Windows32)
478
              по умолчанию нет, поддерживает инверсию
479
              только под windows
480
              для DLL устанавливается в да
481
  /WIB=#####  установить адрес image base
482
              по умолчанию 0x400000
483
  /WMB        создавать Windows-файл с единым блоком
484
              по умолчанию да, поддерживает инверсию
485
              только под windows
486
              для DLL устанавливается в нет
487
  /WORDS      выдать список зарезервированных идентификаторов
488
  /WS=name    указывает имя файла используемого в качестве stub под windows.
489
  /X          запретить вставлять в код SPHINXC-- сигнатуру
490
              по умолчанию разрешено, поддерживает инверсию
491
              отключается если есть J0
492
 
493
      Примечание: выражение поддерживает инверсию означает, что для данной
494
  опции можно использовать и противоположное значение с помощью символа -
495
  после опции. Пример:
496
 
497
  /WFA-
498
 
499
       Параметры командной строки можно писать как большими, так и
500
  маленькими буквами.
501
Return to contents.
502
 
503
 
504
505
    2.1.1 /ON - Оптимизация числовых выражений.
506
507
 
508
        При включении в командную строку опции /ON или в файл C--.INI строчки
509
    ON, компилятор будет анализировать операции над числами и где это
510
    можно, сокращать число операций. Например:
511
 
512
     Строка до оптимизации  | После оптимизации
513
    -----------------------------------------------
514
      AX = var + 7 - 3;     | AX = var + 4;
515
      AX = var * 2 * 5;     | AX = var * 10;
516
      AX = var * 2 / 4;     | AX = var / 2;
517
      AX = var * 10 / 2;    | AX = var * 5;
518
      AX = var / 2 / 3;     | AX = var / 6;
519
      AX = var / 4 * 8;     | AX = var * 2;
520
      AX = var / 16 * 16;   | AX = var;
521
 
522
        Возможные отрицательные последствия:
523
        Применение этой оптимизации может иметь и негативные последствия.
524
    Например, если Вам нужно выровнять значение переменной на границу
525
    параграфа, Вы напишите строку:
526
 
527
    var = var / 16 * 16;
528
 
529
    но после оптимизации будет
530
 
531
    var = var;
532
 
533
    т.е. выравнивание не будет  произведено. Этого можно избежать, если
534
    разбить это выражение на два:
535
 
536
    var = var / 16;
537
    var = var * 16;
538
 
539
    тогда оптимизация не будет произведена. Но для получения более
540
    компактного кода лучше будет записать так:
541
 
542
    AX = var;
543
    AX = AX / 16;
544
    AX = AX * 16;
545
    var = AX;
546
Return to contents.
547
 
548
 
549
550
    2.1.2 /DE - Временное расширение разрядности переменной.
551
552
 
553
        Как известно, после умножения может произойти переполнение, т.е
554
    разрядность результата может превысить разрядность исходных операндов и
555
    произойдет искажение результата. Частично решить эту проблему Вам поможет
556
    опция командной строки /DE или строка DE в файле C--.INI. После команды
557
    умножения компилятор будет просматривать остаток строки и если обнаружит,
558
    что расширение разрядности может быть востребовано (востребовать
559
    расширенную разрядность могут операции деления и вычисления остатка), то
560
    будут приняты меры по ее сохранению. Например:
561
 
562
      a = b*c+d/e; //здесь будет включена поддержка расширения разрядности
563
      a = b*c+d*e; //здесь поддержки расширения разрядности не будет.
564
 
565
        Однако применение этой опции может иметь и негативные последствия.
566
    Покажу это на примере:
567
 
568
    пусть имеется выражение
569
 
570
      a = b * c / d;
571
 
572
    если значения переменных b = 0xC000, c = 0x1000, d=0x10, после запуска
573
    такая программа зависнет с сообщением о том, что произошло переполнение
574
    при делении.
575
Return to contents.
576
 
577
 
578
579
    2.1.3 /ARGC - Альтернативный обработчик командной строки.
580
581
 
582
        Отличие этого обработчика командной строки от parsecommandline
583
    заключается в том, что при вызове PARAMSTR(0); Вы получите адрес строки в
584
    которой указан путь и имя запущенной программы. Следующие вызовы этой
585
    процедуры с увеличивающимся параметром будут возвращать адреса слов
586
    командной строки. А вызов процедуры PARAMCOUNT вернет Вам число слов в
587
    командной строке плюс один.
588
 
589
        Альтернативный обработчик командной строки включается директивой
590
    ?argc TRUE или из командной строки компилятора ключом /argc или
591
    строчкой argc в файле C--.INI.
592
Return to contents.
593
 
594
 
595
596
    2.1.4 /OST - слияние одинаковых строковых констант.
597
598
 
599
        Если этот режим оптимизации будет активизирован, то компилятор будет
600
    запоминать все строковые константы и при обнаружении одинаковых в код
601
    файла не будет вставлена повторная строковая константа, а будет сделана
602
    ссылка на первую, обнаруженную ранее строковую константу. В оптимизации
603
    участвуют только неименованные строковые константы. Т.е. если массив или
604
    структура будет инициализированы строкой, то такая строка не будет
605
    участвовать в процессе инициализации, так эта строка может быть изменена
606
    в процессе работы программы. Пример:
607
 
608
      char var="test";  //эта строка не будет участвовать в процессе
609
                        //оптимизации.
610
 
611
      void proc(){
612
        WRITESTR("test");	// эта строка будет участвовать в оптимизации.
613
        AX="test";          // переменной AX будет присвоен адрес строки,
614
                            // которая была вставлена в код программы в
615
                            // предыдущей строке.
616
      }
617
 
618
        Обо всех случаях обнаружения повторной строки компилятор будет
619
    выдавать предупреждения.
620
 
621
        Включается этот режим оптимизации либо с командной строки /ost, либо
622
    директивой #pragma option ost, либо строкой в файле c--.ini - ost.
623
    Отключить, включенный ранее, этот режим можно директивой #pragma option ost-.
624
Return to contents.
625
 
626
 
627
628
    2.1.5 /D - установка идентификатора в TRUE из командной строки.
629
630
 
631
        Если Вы написали программу, которая может компилироваться по разному,
632
    в зависимости от состояния некоторых идентификаторов (используется режим
633
    условной компиляции), то Вам очень может пригодится эта опция.
634
    Устанавливая с командной строки различные идентификаторы, Вы можете
635
    получать различные варианты программы, не редактируя исходный текст
636
    программы.
637
 
638
        Идентификатор вводится с командной строки ключом /d=idname.
639
Return to contents.
640
 
641
 
642
643
    2.1.6 /IA - упрощенный ввод ассемблерных инструкций.
644
645
 
646
        Стало возможным использовать ассемблерные инструкции без префикса $
647
    и вне блока asm. Этот режим включается: с командной строки опцией /ia;
648
    в файле конфигурации строкой ia или директивой #pragma option ia.
649
 
650
        Когда этот режим включен, все имена ассемблерных инструкций становятся
651
    зарезервированными словами, т.е. Вы не сможете эти имена использовать в
652
    качестве имен переменных или процедур. Ассемблерные инструкции компилятор
653
    распознает независимо от того, написаны они маленькими или большими
654
    буквами.
655
Return to contents.
656
 
657
 
658
659
    2.1.7 /CRI - пропуск повторно включаемого файла.
660
661
 
662
        Чаще всего, повторно включать файл в компилируемый проект, нет
663
    необходимости, но это иногда происходит из-за того, что некоторые
664
    включаемые файлы сами включают другие файлы. Чтобы этого не происходило
665
    приходится делать проверку на повторную загрузку файла. Теперь эту
666
    функцию берет на себя компилятор и у Вас отпадает необходимость делать
667
    эту проверку.
668
 
669
        Но иногда (очень редко) возникает потребность сделать повторное
670
    включение файла. Для этого в компиляторе есть опция командной строки
671
    /cri-, которая запрещает компилятору делать проверку на повторное
672
    включение. Соответственно, для c--.ini файла, это можно сделать строкой
673
    cri- или директивой в компилируемом файле - #pragma option cri-.
674
Return to contents.
675
 
676
 
677
678
    2.1.8 /IND - импорт имен процедур из DLL.
679
680
 
681
        Если Вы хотите в своей программе использовать DLL, для которой нет
682
    заголовочного файла с описанием процедур, то компилятор может
683
    импортировать имена из этой DLL. Для этого Вам надо указать имя этой
684
    библиотеки либо через опцию командной строки /ind=name.dll, либо в
685
    файле INI строкой 'ind=name.dll', либо через директиву '#pragma option
686
    ind=name.dll'.
687
 
688
        К недостатком такого способа получения имен можно отнести то, что при
689
    компиляции программы библиотека, из которой импортируются имена,
690
    обязательно должна присутствовать в компьютере. Также, если имена в
691
    библиотеке написаны без суффикса '@number', компилятор не будет
692
    контролировать число параметров передаваемых процедуре. И, к сожалению,
693
    компилятор умеет импортировать имена из библиотек имеющих только формат
694
    PE-файла.
695
Return to contents.
696
 
697
 
698
699
    2.1.9 /WS - задать имя stub файла для программ под windows.
700
701
 
702
        Как известно, в программах под windows есть DOS заглушка, называемая
703
    stub, которой передается управление при запуске такой программы в чистом
704
    DOS-е. Обычно такая заглушка выводит на экран сообщение о том, что эту
705
    программу надо запускать в среде windows.
706
 
707
        Вы можете вместо стандартного stub использовать свой. Для этого Вам
708
    необходимо указать имя 16-битного EXE-файла либо через опцию командной
709
    строки /ws=filename, либо строкой в INI-файле ws=filename, либо
710
    директивой #pragma option ws=filename.
711
 
712
        Таким образом, у Вас появилась возможность создавать программы,
713
    работающие и под DOS и под windows.
714
Return to contents.
715
 
716
 
717
718
    2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции.
719
720
 
721
        Секция .bss создается автоматически при компиляции программ с ключом
722
    /w32. Если Вы хотите иметь эту секцию и при компиляции программ с
723
    ключами /w32c или /dll Вам необходимо добавить либо в командной
724
    строке опцию /wbss, либо строку wbss в INI-файле, либо директиву
725
    #pragma option wbss.
726
 
727
        Использование секции .bss практически не влияет на размер получаемого
728
    файла. Теоретически, для процессоров, у которых есть отдельный кэш для
729
    данных, использование секции .bss, должно повышать скорость работы
730
    программы.
731
Return to contents.
732
 
733
 
734
735
    2.1.11 /DBG - создание отладочной информации.
736
737
 
738
        Если при компиляции программы в командную строку добавить ключ /dbg,
739
    или в файл конфигурации c--.ini добавить строку dbg, то компилятор после
740
    окончания компиляции создаст файл с отладочной информацией. Этот файл
741
    имеет имя главного модуля и имеет расширение *.tds.
742
 
743
        Отладочная информация создаваемая компилятором C-- совместима с
744
    отладочной информацией создаваемой компиляторами фирмы Borland. Но, пока,
745
    эта информация реализована еще не в полном объеме. Создаваемой сейчас
746
    отладочной информации достаточно для проведения простейшей отладки
747
    программы.
748
 
749
        Для 16-битных программ под DOS для отладки надо использовать Turbo
750
    Debugger из пакета Borland C v4.5 или лучше (файл td.exe).
751
 
752
        Для программ под Windows надо использовать 32-битный отладчик из этого
753
    же пакета (файл td32.exe).
754
 
755
        Для 32-битных программ, использующих расширитель DOS применять для
756
    отладки Turbo Debugger невозможно. Но, может быть я не знаю, как это
757
    делать. Если Вы знаете, как создавать 32-битные программы с
758
    DOS-расширителем компиляторами фирмы Borland с включением в них отладочной
759
    информации, то расскажите мне. А я попробую применить это для C--.
760
Return to contents.
761
 
762
 
763
764
    2.1.12 /J0 /J1 /J2
765
766
 
767
        Синонимом ключей /J0 /J1 /J2 является директива #jumptomain с
768
    параметрами NONE, SHORT и NEAR соответственно.
769
 
770
        Директива #jumptomain выполняет немного различные функции в
771
    зависимости от типа выходного файла.
772
 
773
        Компиляция файла типа *.com и *.exe модель памяти tiny:
774
 
775
      #jumptomain NONE (-j0) - в этом случае по окончании кода начальной
776
    инициализации программы не генерируется jmp на процедуру main. Эту
777
    директиву следует использовать в случае, если до процедуры main нет других
778
    не динамических процедур и инициализированных переменных.
779
 
780
      #jumptomain SHORT (-j1) - в этом случае по окончании кода начальной
781
    инициализации генерируется короткий jmp на процедуру main. Эту директиву
782
    следует использовать, если до процедуры main находится не более 128 байт
783
    кода и данных.
784
 
785
      #jumptomain NEAR (-j2) - это состояние устанавливается по умолчанию. При
786
    этом генерируется близкий jmp на процедуру main.
787
 
788
        Компиляция файлов *.exe (ключи -exe -d32 -w32 -w32c):
789
 
790
      #jumptomain NONE (-j0) - в этом случае код начальной инициализации
791
    программы не генерируется и управление при запуске передается сразу на
792
    процедуру main.
793
 
794
      Во всех остальных случаях генерируется код начальной инициализации и
795
    управление на процедуру main передается инструкцией call.
796
 
797
        Компиляция файлов *.dll:
798
 
799
      #jumptomain NONE (-j0) - в этом случае код начальной инициализации
800
    программы не генерируется и управление при запуске передается сразу на
801
    процедуру main.
802
 
803
      Во всех остальных случаях генерируется код заглушки и управление на
804
    процедуру main не передается. Фактически процедура main в этом случае не
805
    нужна.
806
 
807
      Процедура main при создании файлов DLL должна выглядеть немного иначе,
808
    чем в других случаях:
809
 
810
    dword main ( dword hInstDLL, reason, reserv )
811
    {
812
      ...
813
    }
814
Return to contents.
815
 
816
 
817
818
    2.1.13 /LST - Создание ассемблерного листинга.
819
820
 
821
        С помощью дополнительной опции командной строки -lst Вы можете
822
    получить вместе с исполнительным файлом и его ассемблерный листинг.
823
    Листинг будет помещен в файл одноименный с исполнительным файлом и
824
    имеющим расширение *.lst.
825
 
826
        Ассемблерный листинг создается независимой от компилятора частью кода
827
    с использованием информации накапливаемой при компиляции программы.
828
Return to contents.
829
 
830
 
831
832
    2.1.14 /ENV - Сохранение адреса переменных окружения.
833
834
 
835
        Если при компиляции программы Вы в командную строку добавите опцию
836
    -ENV или в файл c--.ini строка ENV, то компилятор добавит в вашу
837
    программу переменную environ, в которой при загрузке будет сохранятся
838
    адрес переменных окружения запускаемой программы. Для программ под
839
    Windows это будет полный адрес, а для остальных в этой переменной будет
840
    сохраняться только адрес сегмента.
841
Return to contents.
842
 
843
 
844
845
    2.1.15 /CPA - Очистка post-области данных.
846
847
 
848
        Переменные, которым в теле программы не было присвоено никакое
849
    значение, не включаются в тело скомпилированной программы. Для них
850
    резервируется память за пределами программы. Но эта память может быть
851
    заполнена произвольной информацией.
852
 
853
        Если Вам необходимо, чтобы неинициализированные переменные при
854
    загрузке программы всегда содержали одно и тоже значение (ноль) -
855
    включите в командную строку опцию -CPA.
856
Return to contents.
857
 
858
 
859
860
    2.1.16 /W - вывод предупреждений.
861
862
 
863
        По умолчанию компилятор не выводит предупреждения и многие даже не
864
    подозревают о существовании такой полезной опции. В C-- предупреждения
865
    фактически являются подсказками для создания оптимальных программ и
866
    зачастую облегчают отладку программ. В предупреждениях компилятор может
867
    сообщить Вам о том, в каком месте можно использовать короткие формы
868
    операторов IF, WHILE, FOR... О том, какие процедуры, переменные и
869
    структуры определенные в вашей программе не были использованы. О том
870
    какие регистры компилятор использовал без вашего ведома и много другой
871
    полезной информации.
872
 
873
        По умолчанию предупреждения выводятся на экран. Но их бывает так
874
    много, что они могут не поместиться на экране. Поэтому в компиляторе есть
875
    опция, по которой все предупреждения выводятся в файл. Имя этого файла
876
    задается в той же опции. Поместив в свой c--.ini файл пару вот этих строк:
877
 
878
    w
879
    wf=warning
880
 
881
        Вы будете получать в файле warning предупреждения.
882
Return to contents.
883
 
884
 
885
886
    2.1.17 /NW - Выборочное отключение типов предупреждений.
887
888
 
889
        Сейчас компилятор может выдавать 12 типов предупреждений и, иногда их
890
    бывает так много, что становится трудно в них ориентироваться. Теперь
891
    можно выборочно запрещать выдачу предупреждений. Для этого в командной
892
    строке (или в файле C--.INI) можно установить опцию /nw=number, где
893
    number - число от 1 до 12. Этим цифрам соответствуют следующие типы
894
    предупреждений:
895
 
896
      1 - Optimize numerical expressions
897
      2 - Compiler used register ..."
898
      3 - Short operator '...' may be used
899
      4 - String '...' repeated
900
      5 - Expansion variable
901
      6 - Signed value returned
902
      7 - '...' defined above, therefore skipped.
903
      8 - Variable/structure/procedure '...' possible not used
904
      9 - Non-initialized variable may have been used
905
     10 - Return flag was destroyed
906
     11 - Code may not be executable
907
     12 - Don't use local/parametric values in inline procedures
908
Return to contents.
909
 
910
 
911
912
    2.1.18 /WSI - короткая таблица импорта.
913
914
 
915
        Таблица импорта обычно состоит в свою очередь из четырех таблиц. Две
916
    таблицы LookUp Table и Import Address Table абсолютно одинаковы.
917
 
918
        Опцией командной строки /WSI Вы можете заставить компилятор
919
    генерировать только одну из этих двух одинаковых таблиц (генерируется
920
    только Import Address Table). Тем самым у Вас получится более компактная
921
    таблица импорта, что приведет, в некоторых случаях, к созданию более
922
    компактного выходного файла.
923
Return to contents.
924
 
925
 
926
927
  2.2 Директивы транслятора.
928
929
 
930
      C-- не содержит препроцессор. Тем не менее, есть несколько функций
931
  очень похожих на функции C препроцессора.
932
 
933
      Они даются как директивы транслятора. Все директивы транслятора
934
  начинаются с вопросительного знака ? либо с символа #. Вот список имеющихся
935
  директив и их назначение:
936
 
937
  ? align [val]                  Выровнять данные программы на четный по
938
                                 умолчанию или на адрес кратный величине val.
939
 
940
  ? aligncode [val]              Выровнять код программы на четный по
941
                                 умолчанию или на адрес кратный величине val.
942
			         Заполнение производится кодом 0x90.
943
 
944
  ? aligner (aligner value)      определить значение байта вставки.
945
 
946
  ? alignword (TRUE or FALSE)    разрешает или запрещает выравнивание на
947
                                 четный адрес переменных типа word и int,
948
                                 значение по умолчанию TRUE.
949
 
950
  ? argc (TRUE or FALSE)         Включить или отключить альтернативный
951
                                 обработчик командной строки.
952
 
953
  ? atexit                       Вставляет в startup код поддержки процедуры
954
                                 ATEXIT().
955
 
956
  ? code32 (TRUE/FALSE)          разрешает/запрещает генерацию 32-битного
957
                                 кода.
958
 
959
  ? codesize                     оптимизация размера кода (в ущерб скорости).
960
 
961
  ? compilerversion min-vers     указывает, компилятор какой версии необходим
962
                                 для компиляции данной программы.
963
 
964
  ? ctrl_c (TRUE or FALSE )      разрешает или запрещает игнорирование
965
                                 нажатия CTRL-C.
966
 
967
  ? dataseg (value)              указывает компилятору сегментный адрес ОЗУ
968
                                 для переменных при компиляции ROM-BIOS.
969
 
970
  ? define (identifier) (token)  определяет идентификатор.
971
 
972
  ? DOSrequired (номер)          устанавливает минимальную требуемую версию
973
                                 DOS:  старший байт - номер версии,
974
                                 младший байт - номер модификации:
975
                                  0x0101 для версии 1.1 DOS
976
                                  0x0315 для версии 3.21 DOS
977
                                  0x0303 для версии 3.3 DOS
978
                                  0x0600 для версии 6.0 DOS
979
                                  0x0602 для версии 6.2 DOS и т.д.
980
 
981
  ? dosstring (TRUE/FALSE)       указывает компилятору, что в качестве
982
                                 терминатора строки надо использовать символ $
983
 
984
  ? else                         генерирует альтернативный код если ?ifdef или
985
                                 ?ifndef принимают значение FALSE (пример
986
                                 использования смотрите в файле FPU.H--)
987
 
988
  ? endif                        указывает на конец действия директив ifdef и
989
                                 ifndef
990
 
991
  ? fastcallapi (FALSE/TRUE)     запретить/разрешить генерацию быстрого вызова
992
                                 API-процедур (по умолчанию разрешено).
993
                                 Директива работает при компиляции программ
994
                                 под Windows.
995
 
996
  ? fixuptable (TRUE/FALSE)      разрешить/запретить создание FixUp таблицы
997
                                 (по умолчанию запрещено). Директива работает
998
                                 при компиляции программ под Windows.
999
 
1000
  ? ifdef (identifier)           если идентификатор определен, то возвращает
1001
                                 TRUE иначе FALSE
1002
 
1003
  ? imagebase value              задает адрес Image Base. По умолчанию этот
1004
                                 адрес  равен 0x400000. Директива работает при
1005
                                 компиляции программ под Windows.
1006
 
1007
  ? ifndef (identifier)          если идентификатор определен, то возвращает
1008
                                 FALSE иначе TRUE
1009
 
1010
  ? include ("filename")         включает другой файл.
1011
 
1012
  ? includepath ("path")         указание компилятору, в какой директории надо
1013
                                 искать включаемые файлы
1014
 
1015
  ? initallvar                   инициализирует 0 все неинициализированные
1016
                                 переменные.
1017
 
1018
  ? jumptomain (NONE, SHORT, NEAR or FALSE)
1019
                                 устанавливает тип перехода к main(),
1020
                                 значение по умолчанию - NEAR.
1021
 
1022
  ? maxerrors (number)           максимальное количество найденных ошибок,
1023
                                 превысив которое транслятор прекращает
1024
                                 работу, значение по умолчанию - 16.
1025
 
1026
  ? movedatarom  (TRUE/FALSE)    указывает компилятору о необходимости
1027
                                 переноса данных из ПЗУ в ОЗУ.
1028
 
1029
  ? parsecommandline (TRUE or FALSE)
1030
                                 включает в программу блок кода для
1031
                                 синтаксического анализа командной строки
1032
                                 значение по умолчанию FALSE.
1033
 
1034
  ? pragma                       может объявлять несколько других директив
1035
 
1036
  ? print (number or string)     выводит на экран строку или число.
1037
 
1038
  ? printhex (number)            выводит на экран число в шестнадцатеричном
1039
                                 коде.
1040
 
1041
  ? randombyte                   вставляет в код программы байт случайного
1042
                                 значения.
1043
 
1044
  ? resize (TRUE or FALSE)       включает функцию изменения после запуска
1045
                                 размера выделенного программе блока памяти
1046
                                 на минимально требуемый объем,
1047
                                 значение по умолчанию TRUE.
1048
 
1049
  ? resizemessage (string)       сообщение, выводимое на экран перед
1050
                                 аварийным прерыванием выполнения программы,
1051
                                 если изменение размера выделенного программе
1052
                                 блока памяти не выполнено.
1053
 
1054
  ? setdinproc                   по этой директиве компилятор немедленно
1055
                                 вставляет в код компилируемой программы все
1056
                                 вызывавшиеся ранее динамические процедуры.
1057
 
1058
  ? sizerom  (value)             указывает компилятору размер ПЗУ.
1059
 
1060
  ? speed                        оптимизация быстродействия (значение
1061
                                 по умолчанию) в ущерб размеру кода.
1062
 
1063
  ? stack (number)               определяет размер стека программы в байтах.
1064
 
1065
  ? startaddress (number)        устанавливает стартовый адрес начала кода,
1066
                                 значение по умолчанию 0x100.
1067
 
1068
  ? startuptomain                в com-файлах размещает startup-код в
1069
                                 процедуре main().
1070
 
1071
  ? startusevar (number)         указывает адрес, с которого разрешено
1072
                                 использовать ячейки памяти под
1073
                                 неинициализированные переменные.
1074
 
1075
  ? sysattribute (значение)      эта директива передает компилятору атрибут
1076
                                 создаваемого драйвера. По умолчанию
1077
                                 устанавливается значение 0x2000.
1078
                                 Действует только с ключом /SYS.
1079
 
1080
  ? sysname <текстовая строка>   эта директива передает компилятору имя
1081
                                 будущего драйвера. По умолчанию
1082
                                 присваивается имя NO_NAME. Длина имени не
1083
                                 более 8 символов.  Действует только с ключом
1084
                                 /SYS.
1085
 
1086
  ? syscommand ,, ...; - эта директива
1087
                                 является обязательной при создании
1088
                                 драйверов. По этой директиве компилятору
1089
                                 передается список имен процедур обработки
1090
                                 команд драйвера. Действует только с ключом
1091
                                 /SYS.
1092
 
1093
  ? warning (TRUE or FALSE)      эта директива разрешает или запрещает выдачу
1094
                                 предупреждений. Директива действует только в
1095
                                 пределах текущего файла и не влияет на
1096
                                 включаемые файлы.
1097
 
1098
  ? winmonoblock FALSE           запрещает размещение таблиц файла формата PE
1099
                                 в одну секцию.
1100
 
1101
  ? undef                        уничтожает константы объявленные директивой
1102
                                 ? define
1103
 
1104
  ? use8086                      ограничивается при генерации объектного кода
1105
                                 командами 8088/8086 (значение по умолчанию).
1106
 
1107
  ? use8088                      ограничивается при генерации объектного кода
1108
                                 командами 8088/8086 (значение по умолчанию).
1109
 
1110
  ? use80186                     допускает при генерации объектного кода
1111
                                 команды и оптимизацию для процессора 80186.
1112
 
1113
  ? use80286                     допускает при генерации объектного кода
1114
                                 команды и оптимизацию для процессора 80286.
1115
 
1116
  ? use80386                     допускает при генерации объектного кода
1117
                                 команды и оптимизацию для процессора 80386.
1118
 
1119
  ? use80486                     допускает при генерации объектного кода
1120
                                 команды и оптимизацию для процессора 80486.
1121
 
1122
  ? usePentium                   допускает при генерации объектного кода
1123
                                 команды и оптимизацию для процессора Pentium.
1124
 
1125
  ? useMMX                       допускает при генерации объектного кода
1126
                                 команды и оптимизацию для процессора Pentium
1127
                                 MMX.
1128
 
1129
  ? usestartup                   разрешает компилятору использовать ячейки
1130
                                 памяти, занимаемые кодом начальной
1131
                                 инициализации программы.
1132
Return to contents.
1133
 
1134
 
1135
1136
    2.2.1 ?ifdef/?ifndef
1137
1138
 
1139
         Ранее директива ?ifdef срабатывала на наличие константы независимо
1140
    от значения ее величины, а директива ?ifndef срабатывала на отсутствие
1141
    константы в компилируемом файле. Теперь ?indef срабатывает лишь на
1142
    константу отличную от FALSE, а ?ifndef срабатывает как на отсутствие
1143
    константы в компилируемом файле, так и на константу имеющую значение
1144
    FALSE.
1145
 
1146
        Для директив ?ifdef/?ifndef зарезервированы константы codesize и
1147
    speed, которые принимают значение TRUE или FALSE в зависимости от режима
1148
    оптимизации. Это будет полезным для создания более гибких библиотек.
1149
 
1150
        Есть возможность проверки типа CPU для которого ведется компиляция.
1151
    Допустимые варианты синтаксиса:
1152
 
1153
    ?ifdef cpu > 1	 //если программа компилируется для CPU выше 80186
1154
    ?ifndef cpu >= 2 // -------//------------- не больше или равно 80286
1155
    ?ifdef cpu == 3  // -------//------------- равно 80386
1156
    ?ifdef cpu != 0  // -------//------------- не равен 8086
1157
    ?ifdef cpu < 3   // -------//------------- хуже 80386
1158
    ?ifdef cpu <= 2  // -------//------------- хуже или равен 80286
1159
 
1160
        Эта директива позволит Вам писать одну процедуру для различных типов
1161
    CPU.
1162
Return to contents.
1163
 
1164
 
1165
1166
    2.2.2 ?initallvar
1167
1168
 
1169
        Директивой ?initallvar TRUE включается режим при котором всем
1170
    неинициализированным переменным будет присвоено нулевое значение и они
1171
    будут располагаться в том месте, где были объявлены. Т.е. практически
1172
    исчезнут неинициализированные переменные. Это может быть полезным при
1173
    написании драйверов и резидентных программ.
1174
 
1175
        Параметр FALSE этой директивы отключает этот режим.
1176
        По умолчанию эта директива установлена в FALSE.
1177
Return to contents.
1178
 
1179
 
1180
1181
    2.2.3 ?usestartup
1182
1183
 
1184
        Директива ?usestartup разрешает компилятору использовать ячейки кода
1185
    начальной инициализации программы (startup) для последующего размещения в
1186
    них неинициализированных переменных. Это может быть полезным для получения
1187
    более компактного кода, как обычных программ, так и в особенности
1188
    резидентных.
1189
 
1190
        Эту директиву применяют только для генерации *.COM файлов.
1191
Return to contents.
1192
 
1193
 
1194
1195
    2.2.4 ?startusevar
1196
1197
 
1198
        Директивой ?startusevar можно указать начальный адрес с которого
1199
    компилятор будет распределять память для неинициализированных переменных.
1200
    Например, получив директиву ?startusevar 0x53 компилятор будет
1201
    располагать неинициализированные переменные, начиная с адреса 0x53. Это
1202
    может быть полезным для получения более компактного кода как для
1203
    резидентных, так и для обычных программ.
1204
 
1205
        Эту директиву применяют только для генерации *.COM файлов.
1206
Return to contents.
1207
 
1208
 
1209
1210
    2.2.5 ?atexit
1211
1212
 
1213
        Директива ?atexit добавляет в startup программы код поддержки
1214
    процедуры ATEXIT, резервирует место для хранения 16 адресов процедур и
1215
    изменяет код процедур ABORT и EXIT.
1216
 
1217
        Процедура ATEXIT регистрирует процедуру, адрес которой передается ей в
1218
    качестве параметра, как процедуру завершения программы. Эта процедура
1219
    будет вызвана в момент завершения программы процедурами ABORT или EXIT
1220
    или инструкцией RET из main.
1221
 
1222
        Всего можно зарегистрировать до 16 процедур. Процедуры вызываются в
1223
    порядке обратном порядку их регистрации.
1224
Return to contents.
1225
 
1226
 
1227
1228
    2.2.6 ?startuptomain
1229
1230
 
1231
        По этой директиве компилятор в начале файла делает jmp на начало
1232
    процедуры main(). Перед началом компиляции этой процедуры компилятор
1233
    начнет компиляцию startup кода и лишь затем будет продолжена компиляция
1234
    процедуры main(). Тем самым startup код окажется не в начале файла, как
1235
    это происходит обычно, а в теле процедуры main(). Это будет полезным при
1236
    компиляции резидентных программ (TSR).
1237
 
1238
        Директива ?startuptomain работает только при компиляции com-файлов.
1239
Return to contents.
1240
 
1241
 
1242
1243
    2.2.7 ?undef
1244
1245
 
1246
        Эта директива уничтожает константы объявленные директивой ?define. Ее
1247
    можно применять для изменения в процессе компиляции значения какой-нибудь
1248
    константы.
1249
Return to contents.
1250
 
1251
 
1252
1253
    2.2.8 ?align и ?aligncode
1254
1255
 
1256
        В C-- существует директива ?align, которая делает однократное
1257
    выравнивание данных на четный адрес. Но если к этой директиве добавить
1258
    число, то выравнивание будет произведено на адрес кратный этому числу.
1259
    Например директива ?align 4 дополнит сегмент данных до адреса кратного
1260
    4. При выравнивании будут вставляться байты, значения которых определяются
1261
    директивой ?aligner, по умолчанию это значение равно нулю. Директива
1262
    ?align производит выравнивание только в сегменте данных. В тех моделях
1263
    памяти, в которых сегмент данных и кода совпадают эту директиву можно
1264
    применять и для выравнивания начала процедур.
1265
 
1266
        Директива ?aligncode [value] делает выравнивание в сегменте кода на
1267
    адрес кратный значению value, по умолчанию на четный адрес. Значение байта
1268
    заполнения в этой директиве является число 0x90 - код инструкции NOP.
1269
    Значение байта заполнения для этой директивы изменить нельзя. Т.о. эту
1270
    директиву можно применять и внутри исполняемого кода. Например, если Вы
1271
    хотите получить быстрый код на 486 процессоре, то рекомендуется делать
1272
    выравнивание начала процедур и циклов на адрес кратный 16.
1273
Return to contents.
1274
 
1275
 
1276
1277
    2.2.9 ?pragma
1278
1279
 
1280
        Директива #pragma это многофункциональнальная директива, которая в
1281
    свою очередь имеет свои директивы:
1282
 
1283
      option
1284
        Директива option позволяет включить в Ваш код опции командной строки
1285
    компилятора. Некоторые опции не могут быть использованы в этой директиве;
1286
    другие должны помещаться в самом начале исходного текста. Пример:
1287
 
1288
      #pragma option w32c
1289
 
1290
        Эта директива объявляет компилятору, что надо создать консольный
1291
    32-битный файл под windows.
1292
 
1293
      startup
1294
       Директивой startup можно указать функцию, которая будет выполнена перед
1295
    запуском процедуры main. Эта директива имеет такой формат:
1296
 
1297
      #pragma startup procname
1298
 
1299
        Количество раз, которое можно применять эту директиву в одной
1300
    программе не ограничено, но реально можно использовать лишь несколько
1301
    тысяч раз.
1302
 
1303
      line
1304
        Директива line выводит на экран номер текущей строки и имя файла.
1305
    Дополнительно может выводиться содержимое строки находящееся после слова
1306
    line. Пример:
1307
 
1308
      #pragma line information
1309
 
1310
        Встретив эту директиву, компилятор выведет на экран номер строки и имя
1311
    файла. Также будет выведено сообщение справа от слова line, если оно
1312
    есть.
1313
 
1314
      resource
1315
        Эта директива может принимать значения start и end. Эти два
1316
    значения выделяют начало и конец блока ресурсов, если вы используете его
1317
    непосредственно в исходном коде файла, а не в отдельном файле. Пример:
1318
 
1319
    #pragma resource start
1320
 
1321
    MyMenu MENU DISCARDABLE
1322
    BEGIN    POPUP "Files",HELP
1323
        BEGIN
1324
            MENUITEM "Open",                        ID_OPEN
1325
            MENUITEM "Save",                        ID_SAVE
1326
            MENUITEM SEPARATOR
1327
            MENUITEM "Exit",                        ID_EXIT
1328
        END
1329
        MENUITEM "Other",                           65535
1330
    END
1331
 
1332
    #pragma resource end
1333
Return to contents.
1334
 
1335
 
1336
1337
3. Константы.
1338
 
1339
  3.1 Числовые константы.
1340
1341
 
1342
      Представление числовых констант в виде десятичных чисел (чисел с
1343
  основанием 10) и шестнадцатеричных чисел (основание счисления 16) полностью
1344
  аналогично языку C.
1345
 
1346
      При двоичном представлении чисел (основание 2) число должно начинаться
1347
  с символов 0b, за которыми без пробела идет последовательность нулей и
1348
  единиц.
1349
 
1350
      При восьмеричном представлении чисел (основание 8) число должно
1351
  начинаться с символов 0o, за которыми без пробела идет последовательность
1352
  цифр.
1353
 
1354
      Вещественное число отличается от целого по наличию в нем точки.
1355
  Начинаться вещественное число должно либо цифрой от 0 до 9, либо знаком
1356
  минус. Необязательной частью вещественного числа является показатель
1357
  степени. Показатель степени отделяется от числа символом e или E.
1358
  Пробелы недопустимы.
1359
 
1360
  Примеры:
1361
    0b11111111 // двоичное представление числа 255
1362
    0x00F // шестнадцатеричное представление числа 15
1363
    0o10 // восьмеричное представление числа 8
1364
    1.234567E-20 // вещественное число
1365
 
1366
      C-- вместе с традиционным C-стилем шестнадцатеричных чисел понимает и
1367
  числа записанные в стиле ассемблера. Для тех, кто вдруг не знает, сообщаю,
1368
  что шестнадцатеричные числа в ассемблере имеют на конце символ h или H.
1369
  Если первый символ шестнадцатеричного числа больше 9, то перед ним
1370
  обязательно должен быть записан символ 0. Примеры:
1371
 
1372
    1234h
1373
    0A000H
1374
 
1375
      К числовым константам можно писать суффиксы L, U и F. Фактически
1376
  эти суффиксы в C-- не играют никакой роли, компилятор их просто
1377
  проглатывает. Пример:
1378
 
1379
  #define DEF  1L
1380
  #define DEF2 2Lu
1381
  #define DEF3 3.0F
1382
 
1383
      Эти суффиксы не зависят от регистра, т.е. их можно писать как
1384
  маленькими, так и большими буквами.
1385
Return to contents.
1386
 
1387
 
1388
1389
  3.2 Символьные константы.
1390
1391
 
1392
      Одиночные символьные константы, как и в C, должны заключаться в
1393
  одиночные кавычки '.
1394
 
1395
      Также как и в C, для обозначения специальных символов служит обратная
1396
  наклонная черта вправо \ с последующим за ней ключевым символом (или
1397
  несколькими символами). Поддерживаются следующие специальные символы:
1398
 
1399
    \a  /* звуковой сигнал */
1400
    \b  /* забой */
1401
    \f  /* перевод  страницы */
1402
    \l  /* перевод строки */
1403
    \n  /* возврат каретки*/
1404
    \r  /* возврат каретки*/
1405
    \t  /* табуляция */
1406
    \x??  /* символ ASCII, соответствующий байтовому представлению,
1407
               состоящему из двух шестнадцатеричных цифр, расположенных
1408
               на месте знаков вопроса */
1409
     \???  /* символ ASCII, соответствующий байтовому представлению,
1410
               состоящему из трех десятичных цифр, расположенных
1411
               на месте знаков вопроса */
1412
 
1413
      Любой другой символ после обратной наклонной черты вправо будет принят
1414
  как простой символ.
1415
 
1416
      Символ "Одиночная кавычка" ' может быть введен при помощи конструкции
1417
  \'
1418
 
1419
      Символ NULL может быть введен как ''
1420
 
1421
      В C-- поддерживаются и многобуквенные символьные константы. Примеры
1422
  многобуквенных символьных констант:
1423
 
1424
         'ab'
1425
         'the'
1426
         'this is large'
1427
 
1428
      Никакого ограничения на число символов в символьной константе не
1429
  накладывается, но различаются только последние 4 символа. Это - максимум,
1430
  который может быть сохранен в 32-разрядной переменной. Например, константы
1431
  this is large и arge - одинаковы.
1432
 
1433
      C-- обрабатывает все символьные константы как числовые значения ASCII
1434
  символов. Для многобуквенных символьных констант первый символ
1435
  соответствует старшим разрядам, таким образом, значение для ab будет
1436
  закодировано как a*256+b.
1437
Return to contents.
1438
 
1439
 
1440
1441
  3.3 Строковые константы.
1442
1443
 
1444
      Строковые константы, как и в C, заключаются в двойные кавычки (").
1445
  Специальные символы внутри строк обозначаются так же, как и в символьных
1446
  константах. Все специальные символы имеют то же значение, что и в
1447
  символьных константах за исключением \n, который имеет значение новая
1448
  строка и заменяет собой пару символов возврат каретки и перевод
1449
  строки.
1450
 
1451
      В настоящее время наибольшая длина строковой константы - 2048 символов,
1452
  включая символ-ограничитель 0, таким образом, максимум 2047 значащих
1453
  символов.
1454
Return to contents.
1455
 
1456
 
1457
1458
  3.4 Постоянные выражения.
1459
1460
 
1461
      Постоянное выражение - одиночная числовая константа или несколько
1462
  числовых констант, связанных между собой операторами. Числовое значение
1463
  выражения вычисляется один раз во время компиляции и далее используется
1464
  только его постоянное значение.
1465
 
1466
      Подобно всем выражениям в C--, постоянные выражения всегда вычисляются
1467
  слева направо, невзирая на правила арифметики! Это совершенно отлично от
1468
  других языков, и при написании выражений надо быть осторожным и помнить,
1469
  что 2+3*2=10 а не 8.
1470
 
1471
      Некоторые примеры постоянных выражений:
1472
  45 & 1 + 3 // равняется 4
1473
  14 - 1 / 2 // равняется 6 (помните целочисленные значения)
1474
  1 * 2 * 3 / 2 + 4 // равняется 7
1475
      Примеры с применением вещественных чисел:
1476
  3.23*1.53+2.0E2 // равняется 204.9419
1477
Return to contents.
1478
 
1479
 
1480
1481
4. Выражения.
1482
 
1483
  4.1 Типы выражений.
1484
1485
 
1486
      Имеются три типа выражений в C--, не считая постоянных выражений. Это
1487
  выражения типа EAX/AX/AL, выражения типа неEAX/AX/AL и условные выражения.
1488
  Все C-- выражения вычисляются слева направо, независимо от старшинства
1489
  входящих в выражение математических операций.
1490
Return to contents.
1491
 
1492
 
1493
1494
  4.2 Выражения типа EAX/AX/AL.
1495
1496
 
1497
      Этот тип выражений применяется в случае, когда его результат может быть
1498
  сохранен в переменной в памяти или в регистре EAX или AX или AL.
1499
 
1500
      Если результат может быть сохранен в переменных типа byte или char,
1501
  используется нотация AL.
1502
 
1503
      Если результат может быть сохранен в переменных типа word или int,
1504
  используется нотация AX.
1505
 
1506
      Если результат может быть сохранен в переменных типа dword, long или
1507
  float, используется нотация EAX.
1508
Return to contents.
1509
 
1510
 
1511
1512
  4.3 Выражения использующие получатель при вычислении выражения.
1513
1514
 
1515
      Если в правой части выражения используется переменная являющаяся
1516
  одновременно и приемником, то такие выражения дают различные результаты в
1517
  зависимости от того является приемник регистром или переменной памяти. Это
1518
  связано с тем, что при вычислении выражения в переменную памяти, вычисление
1519
  производится сначала в регистр EAX/AX/AL, и лишь после окончания вычисления
1520
  результат будет записан в приемник. Если же приемником является регистр, то
1521
  его значение будет меняться после каждой операции вычисления. Пример:
1522
 
1523
  int var;
1524
    var = BX = 2;
1525
    var = 3 + var; // результатом будет 5
1526
    BX = 3 + BX;   // результатом будет 6
1527
Return to contents.
1528
 
1529
 
1530
1531
  4.4 Не - EAX/AX/AL выражения.
1532
1533
 
1534
      Этот тип выражений применяется в случае, когда его результат должен
1535
  быть сохранен в любом другом регистре, отличном от аккумулятора EAX, AX
1536
  или AL. В процессе вычисления выражения этого типа меняется только
1537
  содержимое указанного регистра-получателя, все другие регистры будут
1538
  сохранены. Если регистром-получателем служит байтовый регистр, а при
1539
  вычислении используются величины размером в слово, одновременно с записью в
1540
  младший байт может быть разрушено содержимое старшего байта
1541
  регистра-получателя.
1542
 
1543
      Это обстоятельство накладывает некоторые ограничения на операции и
1544
  операнды, допустимые в выражениях типа не EAX/AX/AL. Внутри выражений
1545
  байтового типа не допускается:
1546
 
1547
      - делать вызовы МАКРОКОМАНД,
1548
      - делать вызовы РЕГИСТРОВЫХ процедур
1549
      - делать вызовы СТЕКОВЫХ процедур
1550
 
1551
      Ранее в не-EAX/AX/AL выражениях было можно использовать лишь
1552
  операции: сложения, вычитания, XOR, OR, AND. Теперь для 16 и 32 битных
1553
  регистров почти все ограничения сняты. Но есть еще ограничения на регистры.
1554
  Например, если в выражении используется сдвиг на значение переменной, а
1555
  приемником являются регистры CX/ECX, то такое выражение компилятор не будет
1556
  компилировать:
1557
 
1558
     CX = var * SI * 3 * var >> 3;  //вызовет сообщение об ошибке
1559
 
1560
      Примечание:  для 8 битных не-AL выражений умножать можно только на
1561
  числа: 0, 1, 2, 4, 8, 16, 32, 64 и 128. Все эти ограничения связаны со
1562
  стремлением не разрушать другие регистры при использовании не-EAX/AX/AL
1563
  выражений.
1564
Return to contents.
1565
 
1566
 
1567
1568
  4.5 Условные выражения.
1569
1570
 
1571
      Условные выражения - выражения, результатом вычисления которых является
1572
  логическое значение да или нет, используемое в операторе if и циклах do {}
1573
  while, while, for.
1574
 
1575
      Имеются два типа условных выражений, простые и сложные.
1576
 
1577
      Возможно логическое объединение условий.
1578
Return to contents.
1579
 
1580
 
1581
1582
    4.5.1 Простые условные выражения.
1583
1584
 
1585
        Простые условные выражения - одиночная лексема или выражение, которое
1586
    примет значение да, если расчетное значение отлично от нуля, или значение
1587
    нет, если расчетное значение равно нулю.
1588
Return to contents.
1589
 
1590
 
1591
1592
    4.5.2 Сложные условные выражения.
1593
1594
 
1595
    Сложные условные выражения имеют следующую форму:
1596
 
1597
         (левая_часть оператор_отношения правая_часть)
1598
 
1599
    Где:
1600
       левая_часть - любое выражение типа AL/AX/EAX или постоянное выражение.
1601
                     Тип выражения определяется по типу первой лексемы
1602
                     (регистра или переменной); значение типа по умолчанию -
1603
                     word для 16-битных программ и dword для 32-битных. Если
1604
                     желателен другой тип, перед выражением ставится
1605
                     соответствующее ключевое слово, определяющее его тип:
1606
                     byte, char, int, long, dword или float
1607
 
1608
       оператор_отношения - любой из операторов отношения:
1609
                     ==, !=, <>, <, >, <=, или >=.
1610
 
1611
       правая_часть - любой одиночный регистр, одиночная переменная или
1612
                     постоянное выражение.
1613
 
1614
    Примеры правильных сложных условных выражений:
1615
 
1616
         (X + y > z)
1617
         (int CX*DX < = 12*3)
1618
         (byte first*second+hold == cnumber)
1619
 
1620
    Примеры недопустимых сложных условных выражений:
1621
 
1622
         (x+y >= x-y) // правая часть не является одиночной лексемой или
1623
                         постоянным выражением.
1624
         (Z = y) // вместо == ошибочно поставлен =
1625
Return to contents.
1626
 
1627
 
1628
1629
  4.6 Изменение типа выражения при присваивании.
1630
1631
 
1632
      Если после знака равенства написать тип отличный от типа вычисляемой
1633
  переменной, то все переменные участвующие в процессе вычисления, будут
1634
  преобразовываться к этому новому типу, и лишь конечный результат будет
1635
  преобразован к типу вычисляемой переменной. Пример:
1636
 
1637
  int i, a;
1638
  long b;
1639
  char c;
1640
 
1641
    i = a * b + c ;
1642
 
1643
      Значения переменных a, b, и c в этом примере перед вычислением будут
1644
  преобразованы к типу int (типу переменной i). Но если записать это
1645
  выражение вот так:
1646
 
1647
    i = long a * b + c ;
1648
 
1649
      то  переменные  a,  b,  и  c  в  этом  примере  перед  вычислением будут
1650
  преобразованы к типу  long, а конечный  результат будет преобразован  к типу
1651
  переменной i - int.
1652
Return to contents.
1653
 
1654
 
1655
1656
  4.7 Вычисление в регистры EAX/AX/AL со знаком.
1657
1658
 
1659
      По умолчанию все вычисления в регистры производятся как с без знаковыми
1660
  величинами.
1661
 
1662
  Например:
1663
 
1664
    int a,b,c;
1665
    AX = a * b / c ;
1666
 
1667
  При этом компилятор генерировал без знаковые инструкции div и mul, так как
1668
  регистры считаются без знаковыми переменными. Если написать вот так:
1669
 
1670
    AX = int a * b / c ;
1671
 
1672
  то компилятор сгенерирует инструкции idiv и imul.
1673
 
1674
       Обращаю ваше внимание, что для регистра AL можно использовать только
1675
  модификатор char, для AX соответственно только int, а для EAX - long. Для
1676
  остальных регистров подобное делать нельзя.
1677
Return to contents.
1678
 
1679
 
1680
1681
5. Идентификаторы.
1682
 
1683
  5.1 Формат идентификатора.
1684
1685
 
1686
      Идентификаторы в C-- должны начинаться или с символа подчеркивания _
1687
  или заглавных или строчных букв. Следующие символы могут быть любой
1688
  комбинацией символов подчеркивания, заглавных или строчных букв или чисел
1689
  (от 0 до 9). Общая длина идентификатора не может превышать 64 символа.
1690
  Символы с кодом больше 0x7A (код символа z) недопустимы.
1691
 
1692
  Примеры допустимых идентификаторов:
1693
 
1694
  _DOG
1695
  Loony12
1696
  HowdYBoys_AND_Girls
1697
  WOW___
1698
  X
1699
 
1700
  Примеры недопустимых идентификаторов:
1701
 
1702
  12bogus                                 /* не может начинаться с числа */
1703
  WowisthisalongidentifieryupitsureisnotOyoulengthismorethat64chars
1704
   /*длина идентификатора превышает 64 */
1705
  Y_es sir                                /* пробелы недопустимы */
1706
  The-end                                 /* дефисы недопустимы */
1707
Return to contents.
1708
 
1709
 
1710
1711
  5.2 Зарезервированные идентификаторы.
1712
1713
 
1714
      Список зарезервированных в C-- идентификаторов, которые не могут
1715
  использоваться как общие идентификаторы, поскольку они уже были определены
1716
  или зарезервированы для других целей:
1717
 
1718
  BREAK  CASE    CONTINUE  ELSE    EXTRACT  FALSE  FOR
1719
  FROM   GOTO    IF        LOOPNZ  RETURN   SWITCH TRUE
1720
  WHILE
1721
 
1722
  CARRYFLAG    MINUSFLAG  NOTCARRYFLAG  NOTOVERFLOW
1723
  NOTZEROFLAG  OVERFLOW   PLUSFLAG      ZEROFLAG
1724
 
1725
  __CODEPTR__ __COMPILER__ __DATAPTR__ __DATESTR__ __DATE__    __DAY__
1726
  __HOUR__    __LINE__     __MINUTE__  __MONTH__   __POSTPTR__ __SECOND__
1727
  __TIME__    __VER1__     __VER2__    __WEEKDAY__ __YEAR__
1728
 
1729
  _export  asm     break   byte      case     cdecl   char       continue
1730
  default  do      dword   else      enum     extern  far        fastcall
1731
  float    for     goto    if        inline   int     interrupt  long
1732
  loop     loopnz  pascal  return    short    signed  sizeof     static
1733
  stdcall  struct  switch  union     unsigned void    while      word
1734
 
1735
  ESCHAR  ESBYTE  ESINT  ESWORD  ESLONG  ESDWORD  ESFLOAT
1736
  CSCHAR  CSBYTE  CSINT  CSWORD  CSLONG  CSDWORD  CSFLOAT
1737
  SSCHAR  SSBYTE  SSINT  SSWORD  SSLONG  SSDWORD  SSFLOAT
1738
  DSCHAR  DSBYTE  DSINT  DSWORD  DSLONG  DSDWORD  DSFLOAT
1739
  FSCHAR  FSBYTE  FSINT  FSWORD  FSLONG  FSDWORD  FSFLOAT
1740
  GSCHAR  GSBYTE  GSINT  GSWORD  GSLONG  GSDWORD  GSFLOAT
1741
 
1742
  AX   CX   DX   BX   SP   BP   SI   DI
1743
  EAX  ECX  EDX  EBX  ESP  EBP  ESI  EDI
1744
  AL   CL   DL   BL   AH   CH   DH   BH
1745
  ES   CS   SS   DS   FS   GS
1746
 
1747
  ST(0)  ST(1)  ST(2)  ST(3)  ST(4)  ST(5)  ST(6)  ST(7)  ST
1748
  st(0)  st(1)  st(2)  st(3)  st(4)  st(5)  st(6)  st(7)  st
1749
1750
      Этот список может быть получен из C-- транслятора в любое время,
1751
  запуском его с опцией /WORDS из командной строки.
1752
 
1753
      Если Вы пользуетесь при компиляции опцией командной строки /ia, которая
1754
  позволяет использовать ассемблерные инструкции не заключая их в блоки asm и
1755
  без префикса $, то все имена ассемблерных инструкций становятся
1756
  зарезервированными словами. Причем имена ассемблерных инструкций компилятор
1757
  различает независимо от того, написаны они маленькими или большими буквами.
1758
 
1759
     Список имен поддерживаемых компилятором ассемблерных инструкции можно
1760
  получить запустив компилятор с опцией /LAI.
1761
 
1762
     Кроме этого в ассемблерных инструкциях становятся зарезервированными
1763
  следующие идентификаторы:
1764
 
1765
  ax   cx   dx   bx   sp   bp   si   di
1766
  eax  ecx  edx  ebx  esp  ebp  esi  edi
1767
  al   cl   dl   bl   ah   ch   dh   bh
1768
  es   cs   ss   ds   fs   gs
1769
 
1770
  DR0   DR1   DR2   DR3   DR4   DR5   DR6   DR7
1771
  CR0   CR1   CR2   CR3   CR4   CR5   CR6   CR7
1772
  TR0   TR1   TR2   TR3   TR4   TR5   TR6   TR7
1773
  MM0   MM1   MM2   MM3   MM4   MM5   MM6   MM7
1774
  XMM0  XMM1  XMM2  XMM3  XMM4  XMM5  XMM6  XMM7
1775
 
1776
  dr0   dr1   dr2   dr3   dr4   dr5   dr6   dr7
1777
  cr0   cr1   cr2   cr3   cr4   cr5   cr6   cr7
1778
  tr0   tr1   tr2   tr3   tr4   tr5   tr6   tr7
1779
  mm0   mm1   mm2   mm3   mm4   mm5   mm6   mm7
1780
  xmm0  xmm1  xmm2  xmm3  xmm4  xmm5  xmm6  xmm7
1781
Return to contents.
1782
 
1783
 
1784
1785
  5.3 Универсальные регистры для 16 и 32-битного режима.
1786
1787
 
1788
      При создании библиотечных процедур очень часто приходится писать
1789
  варианты процедуры для работы в 16-битном и 32-битном режимах, которые
1790
  отличаются друг от друга лишь использованием в них либо 16-битных либо
1791
  32-битных регистров соответственно. Но можно писать лишь одну процедуру,
1792
  используя в ней новый синтаксис регистров. Если компилятор встретит вот
1793
  такой синтаксис:
1794
 
1795
    (E)AX=0;
1796
 
1797
      то компилятор будет использовать при компиляции 16-битного кода регистр
1798
  AX, а при компиляции 32-битного кода регистр EAX.
1799
 
1800
      Использование автоматических регистров позволит упростить библиотечные
1801
  файлы и сделать их более понятными.
1802
Return to contents.
1803
 
1804
 
1805
1806
  5.4 Предопределенные идентификаторы.
1807
1808
 
1809
      Идентификаторы, определяемые компилятором в зависимости от режима
1810
  компиляции:
1811
 
1812
  __TLS__     идет компиляция под windows (w32, w32c, dll).
1813
  __DLL__     идет компиляция dll.
1814
  __CONSOLE__ идет компиляция консольного приложения windows
1815
  __WIN32__   идет компиляция GUI-шного приложения
1816
  __FLAT__    компилируется 32-битный код.
1817
  __MSDOS__   компилируется 16-битный код.
1818
  __TINY__    используется модель памяти tiny в 16-битном режиме
1819
  __SMALL__   используется модель памяти small в 16-битном режиме
1820
  __DOS32__   компилируется 32-битный код под DOS (d32)
1821
  __COM__     компилируется com-файл
1822
  __SYS__     компилируется sys-файл
1823
  __ROM__     компилируется rom-файл
1824
  __OBJ__     компилируется obj-файл
1825
  __TEXE__    компилируется exe-файл модели tiny
1826
  __EXE__     компилируется exe-файл модели small
1827
  __MEOS__    компилируется исполняемый файл для MenuetOS
1828
  codesize    компиляция ведется с оптимизацией на размер кода
1829
  speed       компиляция ведется с оптимизацией на быстродействие кода
1830
  cpu         определяет тип процессора для которого ведется компиляция:
1831
 
1832
  	    1 - 80186
1833
  	    2 - 80286
1834
  	    3 - 80386
1835
  	    4 - 80486
1836
  	    5 - Pentium
1837
  	    6 - Pentium MMX
1838
  	    7 - Pentium II
1839
 
1840
      Эти идентификаторы могут быть проверены директивами #ifdef или #ifndef.
1841
  Идентификатор cpu может быть использован лишь с операторами проверки
1842
  условий:
1843
 
1844
  #ifdef cpu > 3  //если тип процессора больше 80386
1845
Return to contents.
1846
 
1847
 
1848
1849
6. Переменные.
1850
 
1851
  6.1 Типы переменных.
1852
1853
 
1854
      В C-- имеется семь типов переменных (именованных областей памяти), это:
1855
  byte, word, dword, char, int, long, float.
1856
 
1857
      Следующая таблица  показывает размер  и диапазон  представляемых величин
1858
  каждого из типов переменной:
1859
 
1860
   NAME   | SIZE  |        VALUE RANGE          |        VALUE RANGE
1861
   тип    |размер |   диапазон представления    |   диапазон представления
1862
          |в байт.|    в десятичной системе     | в шестнадцатеричной системе
1863
  ---------------------------------------------------------------------------
1864
  byte    |   1   |           0 to 255          |        0x00 to 0xFF
1865
  word    |   2   |           0 to 65535        |      0x0000 to 0xFFFF
1866
  dword   |   4   |           0 to 4294967295   |  0x00000000 to 0xFFFFFFFF
1867
  char    |   1   |        -128 to 127          |        0x80 to 0x7F
1868
  int     |   2   |      -32768 to 32767        |      0x8000 to 0x7FFF
1869
  long    |   4   | -2147483648 to 2147483647   |  0x80000000 to 0x7FFFFFFF
1870
  float   |   4   |    -3,37E38 to +3,37E38     |  0xFF7FFFFF to 0x7FFFFFFF
1871
 
1872
      Примечание:  для работы с типами float, dword и long используются
1873
  32-разрядные целочисленные команды, следовательно, для их выполнения нужно
1874
  иметь процессор не хуже 80386, что сейчас не является большой проблемой.
1875
 
1876
      Для совместимости со стандартом, принятом в языке C, введены
1877
  новые зарезервированные слова: short, signed, unsigned. Для типа int
1878
  в 32-битном режиме изменена разрядность. Вот таблица всех вариантов новых
1879
  типов данных:
1880
 
1881
  ---------------------------------------------------------
1882
  |   полный тип     |допустимые сокращения|старые аналоги|
1883
  ---------------------------------------------------------
1884
  |signed char       |char                 |  char        |
1885
  |signed int        |signed, int          |  int/long    |
1886
  |signed short int  |short, signed short  |  int         |
1887
  |signed long int   |long, signed long    |  long        |
1888
  |unsigned char     |---                  |  byte        |
1889
  |unsigned int      |unsigned             |  word/dword  |
1890
  |unsigned short int|unsigned short       |  word        |
1891
  |unsigned long int |unsigned long        |  dword       |
1892
  ---------------------------------------------------------
1893
 
1894
      Старые типы byte, word и dword поддерживаются по прежнему и имеют
1895
  функционально прежнее значение. Изменения коснулись лишь типа int. Он в
1896
  16-битном режиме, также как и тип unsigned int, имеет 16-битный размер, а
1897
  в 32-битном режиме эти оба типа имеют размер в 32-бита. На первый взгляд
1898
  такие свойства типа int вносят некоторую путаницу, но это дает большой
1899
  выигрыш при использовании этого типа в библиотечных файлах, которые могут
1900
  быть использованы при компиляции 16-битных и 32-битных программ.
1901
Return to contents.
1902
 
1903
 
1904
1905
  6.2 Объявление переменных.
1906
1907
 
1908
      Синтаксис для объявления переменных следующий:
1909
 
1910
  variable-type identifier;
1911
 
1912
  где variable-type - char, byte, int, word, long, dword или float.
1913
 
1914
      Одновременно могут быть объявлены несколько идентификаторов одного типа:
1915
 
1916
  variable-type identifier1, identifier2, ... , identifierN;
1917
 
1918
      Одномерные массивы могут быть объявлены следующим образом:
1919
 
1920
  variable-type identifier[elements];
1921
 
1922
  где elements -  постоянное выражение для  количества переменных этого  типа,
1923
  объединенных в массив.
1924
 
1925
      Инициализированные массивы можно объявлять без указания числа
1926
  элементов. При этом будет создан массив по фактическому числу элементов.
1927
 
1928
  variable-type identifier[] = { const1, const2 };
1929
 
1930
      Переменные при объявлении могут быть проинициализированы следующим
1931
  образом:
1932
 
1933
  variable-type identifier = value;
1934
 
1935
      Некоторые примеры глобальных объявлений:
1936
  byte i,j;    /* объявляет две переменные типа byte с именами i и j */
1937
  word see[10] /* объявляет массив с именем see, состоящий из 10
1938
                  элементов типа word */
1939
  int h,x[27]  /* объявляет, переменную типа int с именем h,
1940
                  и массив с именем x, состоящий из 27 элементов типа int */
1941
  long size=0; /* объявлена переменная типа long с именем size и ей присвоено
1942
                  значение 0. */
1943
Return to contents.
1944
 
1945
 
1946
1947
  6.3 Глобальные переменные.
1948
1949
 
1950
      Глобальные переменные - это переменные, область действия которых
1951
  распространяется на всю программу. В C-- использовать глобальные переменные
1952
  можно в процедурах, расположенных ниже места ее объявления. Т.е. если Вы
1953
  пишите процедуру, в которой используете переменную var, а саму переменную
1954
  объявляете ниже текста процедуры, то компилятор выдаст ошибку. Это связано
1955
  с тем, что компилятор может знать тип переменной только после их
1956
  объявления.  Но для таких переменных можно использовать взятие их адреса,
1957
  так как адрес переменной не зависит от его типа. Пример:
1958
 
1959
  void Proc(){
1960
    gvar = 0; /* компилятор выдаст сообщение об ошибке, т.к. он еще не знает
1961
                 типа переменной gvar */
1962
    AX = #gvar; /* несмотря на то, что компилятор не знает и адреса этой
1963
                   переменной такое выражение будет откомпилировано */
1964
  }
1965
  int gvar;
1966
 
1967
      Но все же ситуация не безнадежна и нам удастся добиться того, чего мы
1968
  задумали. В этом нам поможет альтернативный синтаксис обращения к
1969
  переменным:
1970
 
1971
  void Proc(){
1972
    DSINT[#gvar] = 0; /* компилятор успешно откомпилирует это выражение т.к.
1973
                         ему теперь известен тип переменной gvar */
1974
  }
1975
  int gvar;
1976
 
1977
      Память под глобальные переменные выделяется в сегменте данных. Если
1978
  переменная при объявлении инициализируется (т.е. ей присвоено какое-то
1979
  значение), то переменная будет включена в код компилируемого файла. Если
1980
  переменная не инициализируется, то место для переменной будет
1981
  зарезервировано сразу же за последним байтом скомпилированной программы.
1982
Return to contents.
1983
 
1984
 
1985
1986
  6.4 Локальные переменные.
1987
1988
 
1989
      Локальные переменные - это переменные область действия которых
1990
  распространяется лишь в пределах одной процедуры. Объявлять локальные
1991
  переменные, в отличии от современных версий C, можно между именем процедуры
1992
  и первой открывающейся фигурной скобкой. Пример:
1993
 
1994
  void PROC ()
1995
  int i;  //объявлена локальная переменная типа int с именем i
1996
  {
1997
      for ( i=0; i<10; i++ ) WRITE(1);
1998
  }
1999
 
2000
      Память под локальные переменные отводится в сегменте стека.
2001
 
2002
      К локальным переменным можно отнести и параметры стековых процедур. Под
2003
  них также отводится память в стеке.
2004
 
2005
      Можно инициализировать локальные переменные при их объявлении. Но есть
2006
  некоторые ограничения. Нельзя инициализировать массивы и многомерные
2007
  структуры. Инициализировать можно одним значением, т.е нельзя при
2008
  инициализации локальных переменных пользоваться перечислением заключенным в
2009
  фигурные скобки и операторами FROM и EXTRACT.
2010
 
2011
      Имена локальных переменных могут совпадать с именами глобальных
2012
  переменных или процедур, но тогда Вы не сможете обратиться к глобальной
2013
  переменной или вызвать одноименную процедуру.
2014
 
2015
      Локальные переменные можно объявлять и в начале блока процедуры. Но
2016
  только до начала тела процедуры. Пример:
2017
 
2018
  void proc(){
2019
  int locproc;  // объявление локальной процедуры
2020
    locproc=0;  // а теперь пошло тело процедуры
2021
  int locproc;  // а на это объявление переменной компилятор выдаст сообщение
2022
                // об ошибке, т.к. уже началось тело процедуры
2023
  }
2024
Return to contents.
2025
 
2026
 
2027
2028
  6.5 Динамические переменные и структуры.
2029
2030
 
2031
      Наряду с уже известными Вам динамическими процедурами в C-- есть
2032
  возможность использовать динамически и переменные и структуры. Динамические
2033
  переменные и структуры обозначаются также как и динамические процедуры -
2034
  символом двоеточия перед началом их объявления. И также как и динамическая
2035
  процедура, динамическая переменная или структура будет вставлена в код,
2036
  лишь в том случае, если она будет использована в программе.
2037
 
2038
      Динамические переменные и структуры найдут применение в библиотеках.
2039
  Использовать их непосредственно в программах нет смысла.
2040
 
2041
      У динамических переменных, структур также как и у процедур, есть один
2042
  недостаток - Вы не сможете знать, в каком месте откомпилированного кода они
2043
  будут расположены, и в каком порядке. Но необходимость это знать бывает
2044
  очень редко.
2045
 
2046
      Динамические инициализированные переменные и структуры в файле будут
2047
  расположены в его самом конце, после динамических процедур. Эту их
2048
  особенность можно использовать, если Вам будет необходимо, чтобы данные не
2049
  были разбросаны среди кода, а были сгруппированы в одном месте.
2050
Return to contents.
2051
 
2052
 
2053
2054
  6.6 Присваивание одного значения нескольким переменным.
2055
2056
 
2057
      Если Вам необходимо присвоить нескольким переменным одинаковые значения:
2058
 
2059
    var1=0;
2060
    var2=0;
2061
    var3=0;
2062
 
2063
      то теперь это можно записать более коротко:
2064
 
2065
    var1=var2=var3=0;
2066
 
2067
      При использовании такой записи генерируется более компактный и более
2068
  быстрый код.
2069
Return to contents.
2070
 
2071
 
2072
2073
  6.7 Переменные типа float.
2074
 
2075
    6.7.1 Формат переменных типа float.
2076
2077
 
2078
        Для представления значений с плавающей точкой в язык C-- введен тип
2079
    float. Этому типу соответствует действительное число одинарной точности
2080
    FPU.
2081
 
2082
        Формат представления данных с плавающей точкой включает три поля:
2083
    знака, мантиссы и порядка. Знак определяется старшим значащим разрядом.
2084
    Поле мантиссы содержит значащие биты числа, а поле порядка содержит
2085
    степень 2 и определяет масштабирующий множитель для мантиссы.
2086
 
2087
    31 30.....23 22........0
2088
    |  |      |  |         |
2089
    |  |      |  -------------- - поле мантиссы
2090
    |  ------------------------ - поле порядка
2091
    --------------------------- - бит знака
2092
Return to contents.
2093
 
2094
 
2095
2096
    6.7.2 Константы с плавающей точкой.
2097
2098
 
2099
        Компилятор отличает вещественное число от целого по наличию в нем
2100
    точки. Начинаться вещественное число должно либо цифрой от 0 до 9, либо
2101
    знаком минус. Необязательной частью вещественного числа является
2102
    показатель степени. Показатель степени отделяется от числа символом e или
2103
    E. Пробелы недопустимы. Вот примеры допустимого синтаксиса:
2104
 
2105
     0.98
2106
     -15.75
2107
     3.14e2
2108
     1.234567E-20
2109
Return to contents.
2110
 
2111
 
2112
2113
    6.7.3 Диапазон допустимых значений.
2114
2115
 
2116
        Вещественное число типа float может находиться в диапазоне от 3.37E38
2117
    до -3.37E38. Минимально близкое к нулю значение равняется 1.17E-38 и
2118
    -1.17E-38. Записывать вещественное число одинарной точности более чем 8
2119
    цифрами не имеет смысла. Показатель степени может принимать значения от
2120
    +38 до -38.
2121
Return to contents.
2122
 
2123
 
2124
2125
    6.7.4 Математические операции.
2126
2127
 
2128
        Компилятор поддерживает 4 основных действия над переменными типа
2129
    float: сложение, вычитание, умножение и деление. Поддерживается также
2130
    инкремент (var++ - увеличение на 1), декремент (var-- - уменьшение на 1),
2131
    смена знака (-var) и обмен значениями (var1 >< var2). Остальные
2132
    математические операции будут реализованы либо уже реализованы во внешних
2133
    библиотеках. При вычислении значения переменной float можно использовать
2134
    и переменные других типов, они будут автоматически преобразованы в тип
2135
    float.
2136
 
2137
        ВНИМАНИЕ! Составные математические  операции выполняются в том
2138
    порядке, в котором они записаны, невзирая на правила арифметики.
2139
Return to contents.
2140
 
2141
 
2142
2143
    6.7.5 Преобразования типов.
2144
2145
 
2146
        При математических операциях конечным итогом которых является
2147
    переменная типа float, все операнды других типов перед вычислением будут
2148
    преобразованы в тип float. При присваивании переменной типа float значения
2149
    переменной другого типа оно также будет преобразовано в тип float.
2150
 
2151
        Если при целочисленных вычислениях одним из операндов будет переменная
2152
    типа float, то из него будет выделена целая часть, которая и примет
2153
    участие в вычислениях. При присваивании целочисленной переменной значения
2154
    переменной типа float, из нее также будет выделена целая часть, которая и
2155
    будет присвоена целочисленной переменной.
2156
Return to contents.
2157
 
2158
 
2159
2160
    6.7.6 Операции сравнения.
2161
2162
 
2163
        Если при операции сравнения левым операндом является переменная или
2164
    выражение типа float, а правым является целочисленное значение, то
2165
    целочисленное значение будет преобразовано в вещественный тип. Если же
2166
    левым операндом является целочисленное выражение или переменная, а правым
2167
    операндом значение типа float, то из правого операнда будет выделена целая
2168
    часть, которая и примет участие в сравнении.
2169
Return to contents.
2170
 
2171
 
2172
2173
    6.7.7 Сравнение переменных типа float с 32-битным регистром.
2174
2175
 
2176
        В регистрах могут содержаться знаковые, без знаковые и вещественные
2177
    данные. По умолчанию считается, что в регистре находится без знаковое целое
2178
    число. При сравнении переменных типа float с 32-битным регистром можно
2179
    указывать тип данных содержащихся в регистре.  Для этой цели можно
2180
    использовать модификаторы: signed, unsigned, float. Примеры:
2181
 
2182
    float f=1.0;
2183
 
2184
    void PROC()
2185
    {
2186
      IF( f < signed ECX)	//в регистре ECX находится знаковое число
2187
      IF( unsigned EBX > f) //в регистре EBX находится без знаковое число
2188
      IF( f == float EAX )  //в EAX находится число формата float
2189
    }
2190
 
2191
        ВНИМАНИЕ!  При операции сравнения с участием переменой типа float,
2192
    содержимое регистра AX будет разрушено.
2193
Return to contents.
2194
 
2195
 
2196
2197
  6.8 Указатели.
2198
2199
 
2200
      В C-- сейчас указатели реализованы не в полном объеме. Поэтому многие
2201
  вещи, которые возможны в обычных языках C, здесь будут недоступны.
2202
 
2203
      Пример применения указателей в C--:
2204
 
2205
  char *string[4]={"string1", "string2", "string3", 0}; //массив указателей
2206
  char *str="string4";
2207
 
2208
  main()
2209
  int i;
2210
  char *tstr;
2211
  {
2212
	FOR(i=0; string[i]!=0; i++){
2213
		WRITESTR(string[i]);
2214
		WRITELN();
2215
	}
2216
	FOR(tstr=str;byte *tstr!=0; tstr++){
2217
		WRITE(byte *tstr);
2218
	}
2219
  }
2220
 
2221
      Указатели можно использовать при передаче параметров процедурам, а в
2222
  самих процедурах в качестве как локальных, так и параметрических
2223
  переменных. Указатели можно также использовать в структурах. Можно
2224
  использовать указатели на указатели. Введена поддержка указателей на
2225
  процедуры:
2226
 
2227
  void (*proc)();  //объявление указателя на процедуру
2228
 
2229
      По умолчанию указатели на процедуру являются указателями на процедуру в
2230
  стиле pascal, независимо от регистра, в котором написано имя процедуры и
2231
  режима компиляции. Если Вам необходимо, чтобы был использован другой тип
2232
  вызова, то его необходимо указать при объявлении указателя на процедуру.
2233
 
2234
      При инициализации указателей компилятор не контролирует то, чем
2235
  инициализируется указатель. Т.е. Вы можете указателю на char присвоить
2236
  указатель на int или указателю на процедуру присвоить адрес переменной.
2237
  Это может вызвать ошибку в работе программы.
2238
Return to contents.
2239
 
2240
 
2241
2242
7. Адресация.
2243
 
2244
  7.1 Относительная адресация.
2245
2246
 
2247
      Изначально индексный доступ к элементам в массивах любого типа в
2248
  компиляторе осуществлялся побайтно, независимо от объявленного типа данных.
2249
  Индексы ограничены форматом поля RM процессора 8086, таким образом,
2250
  доступны только следующие форматы индексов (где индекс - значение
2251
  16-разрядной константы или постоянного выражения):
2252
 
2253
        variable[index]
2254
        variable[index+BX+SI]
2255
        variable[index+BX+DI]
2256
        variable[index+BP+SI]
2257
        variable[index+BP+DI]
2258
        variable[index+SI]
2259
        variable[index+DI]
2260
        variable[index+BP]
2261
        variable[index+BX]
2262
 
2263
      Начиная с версии 0.210, появилась возможность использовать в качестве
2264
  индекса переменных типа char byte int word long dword. При этом
2265
  доступ к элементам массива осуществляется в зависимости от объявленного типа
2266
  массива.
2267
 
2268
      Также начиная с версии 0.210 появилась возможность использовать в
2269
  качестве индексных и базовых регистров при относительной адресации любые
2270
  32-битные регистры.
2271
 
2272
      Если Вы для адресации к элементам массива будете использовать регистры и
2273
  числовые константы, из которых можно получить поле RM для инструкций 8086
2274
  процессора или комбинацию полей RM BASE и SIB для 80386 процессора, то
2275
  компилятор будет использовать эти регистры для генерации инструкции с этими
2276
  полями. В результате Вы получите относительную побайтную адресацию к
2277
  элементам массива.
2278
 
2279
      Если же из этих регистров невозможно получить поля RM, BASE, SIB,
2280
  или для адресации будет использована переменная, то компилятор сначала
2281
  вычислит это выражение в регистр (E)SI или другой, подходящий регистр, а
2282
  затем умножит содержимое этого регистра на разрядность Вашего массива. Таким
2283
  образом, в этом случае вы будете иметь поэлементную адресацию в массиве.
2284
  Пример:
2285
 
2286
    AX = var [ 5 ];
2287
    AX = var [ BX + 5 ];
2288
    AX = var [ BX + CX ];
2289
    AX = var [ i ];
2290
 
2291
      Компилятор сгенерирует следующий код:
2292
  test.c-- 7: AX=var[5];
2293
  0100 A12501                   mov     ax,[125h]
2294
 
2295
  test.c-- 8: AX=var[BX+5];
2296
  0103 8B872501                 mov     ax,[bx+125h]
2297
 
2298
  test.c-- 9: AX=var[BX+CX];
2299
  0107 89DE                     mov     si,bx
2300
  0109 01CE                     add     si,cx
2301
  010B 01F6                     add     si,si
2302
  010D 8B842001                 mov     ax,[si+120h]
2303
 
2304
  test.c-- 10: AX=var[i];
2305
  0111 8B362201                 mov     si,[122h]
2306
  0115 01F6                     add     si,si
2307
  0117 8B842001                 mov     ax,[si+120h]
2308
 
2309
      Как Вы видите, первые два выражения были преобразованы в одну
2310
  ассемблерную инструкцию, и получилась побайтная адресация. В двух следующих
2311
  выражениях получить одну ассемблерную инструкцию не удалось и компилятор
2312
  применил для этих выражений поэлементную адресацию.
2313
 
2314
      Такой двойственный подход реализован с целью сохранения совместимости
2315
  новых возможностей с предыдущими.
2316
 
2317
      Несмотря на кажущуюся для неискушенного пользователя путаницу, этот
2318
  механизм легко понять и запомнить по следующему простому правилу: если Вы
2319
  используете в качестве индекса только цифровое значение или регистр BX, SI,
2320
  DI, BP или любой 32-битный регистр, то компилятор сгенерирует код с
2321
  побайтной адресацией. Если же в качестве индекса будет использована
2322
  переменная, то компилятор сгенерирует код с поэлементной адресацией. Если
2323
  же Вы хорошо знакомы с ассемблером, то Вам не составит большого труда
2324
  понять в каких случаях Вы получите побайтную, а в каких поэлементную
2325
  адресацию.
2326
 
2327
      Иногда требуется иметь побайтный доступ к элементам массива используя в
2328
  качестве индекса переменную. Например
2329
 
2330
    AX=var[i];
2331
 
2332
      Для этого выражения будет сгенерирована поэлементная адресация, а нам
2333
  нужна побайтовая. Для этого можно написать так:
2334
 
2335
    SI=i;
2336
    AX=var[SI];
2337
 
2338
      Но можно это записать короче:
2339
 
2340
    AX=DSWORD[#var+i];
2341
 
2342
      В обоих этих случаях Вы получите побайтную адресацию к элементам массива
2343
  var. В первом варианте Вы сможете контролировать какой регистр будет
2344
  использован в качестве индекса, а во втором варианте компилятор будет сам
2345
  выбирать регистр для использования в качестве индекса.
2346
 
2347
      Важно всегда помнить о двойственном подходе компилятора к вычислению
2348
  адреса в массиве. Еще раз кратко:  если Вы в массиве адресуетесь используя
2349
  числовую константу или регистры BX,DI,SI,BP компилятор использует эти
2350
  значения без изменения.  Во всех других случаях будет коррекция значения в
2351
  зависимости от типа массива.
2352
Return to contents.
2353
 
2354
 
2355
2356
  7.2 Абсолютная адресация.
2357
2358
 
2359
      Абсолютная адресация также возможна.  Действуют те же самые ограничения
2360
  на индексы, что и при относительной адресации.
2361
 
2362
      Вычисленный индекс будет абсолютен в сегменте, регистр которого указан.
2363
  Можно указывать любой из регистров DS, CS, SS и ES. На процессорах 80386 и
2364
  более новых можно указывать также регистры FS и GS.
2365
 
2366
      Синтаксис - точно такой же, как и в относительной адресации, за
2367
  исключением того, что указывается не переменная, а сегмент и тип данных.
2368
  Могут применяться следующие указатели:
2369
 
2370
             // адресация в сегменте данных
2371
         DSBYTE  [смещение] // адресует байт в сегменте DS
2372
         DSWORD  [смещение] // адресует слово в сегменте DS
2373
         DSCHAR  [смещение] // адресует char в сегменте DS
2374
         DSINT   [смещение] // адресует int в сегменте DS
2375
         DSDWORD [смещение] // адресует dword в сегменте DS
2376
         DSLONG  [смещение] // адресует long в сегменте DS
2377
         DSFLOAT [смещение] // адресует float в сегменте DS
2378
 
2379
             // адресация в сегменте кода
2380
         CSBYTE  [смещение] // адресует байт в сегменте CS
2381
         CSWORD  [смещение] // адресует слово в сегменте CS
2382
         CSCHAR  [смещение] // адресует char в сегменте CS
2383
         CSINT   [смещение] // адресует int в сегменте CS
2384
         CSDWORD [смещение] // адресует dword в сегменте CS
2385
         CSLONG  [смещение] // адресует long в сегменте CS
2386
         CSFLOAT [смещение] // адресует float в сегменте CS
2387
 
2388
             // адресация в сегменте стека
2389
         SSBYTE  [смещение] // адресует байт в сегменте SS
2390
         SSWORD  [смещение] // адресует слово в сегменте SS
2391
         SSCHAR  [смещение] // адресует char в сегменте SS
2392
         SSINT   [смещение] // адресует int в сегменте SS
2393
         SSDWORD [смещение] // адресует dword в сегменте SS
2394
         SSLONG  [смещение] // адресует long в сегменте SS
2395
         SSFLOAT [смещение] // адресует float в сегменте SS
2396
 
2397
             // адресация в дополнительном сегменте данных
2398
         ESBYTE  [смещение] // адресует байт в сегменте ES
2399
         ESWORD  [смещение] // адресует слово в сегменте ES
2400
         ESCHAR  [смещение] // адресует char в сегменте ES
2401
         ESINT   [смещение] // адресует int в сегменте ES
2402
         ESDWORD [смещение] // адресует dword в сегменте ES
2403
         ESLONG  [смещение] // адресует long в сегменте ES
2404
         ESFLOAT [смещение] // адресует float в сегменте ES
2405
 
2406
             // адресация в дополнительном сегменте 2 (80386) +
2407
         FSBYTE  [смещение] // адресует байт в сегменте FS
2408
         FSWORD  [смещение] // адресует слово в сегменте FS
2409
         FSCHAR  [смещение] // адресует char в сегменте FS
2410
         FSINT   [смещение] // адресует int в сегменте FS
2411
         FSDWORD [смещение] // адресует dword в сегменте FS
2412
         FSLONG  [смещение] // адресует long в сегменте FS
2413
         FSFLOAT [смещение] // адресует float в сегменте FS
2414
 
2415
             // адресация в дополнительном сегменте 3 (80386) +
2416
         GSBYTE  [смещение] // адресуют байт в сегменте GS
2417
         GSWORD  [смещение] // адресуют слово в сегменте GS
2418
         GSCHAR  [смещение] // адресуют char в сегменте GS
2419
         GSINT   [смещение] // адресуют int в сегменте GS
2420
         GSDWORD [смещение] // адресуют dword в сегменте GS
2421
         GSLONG  [смещение] // адресуют long в сегменте GS
2422
         GSFLOAT [смещение] // адресует float в сегменте GS
2423
 
2424
  Примеры:
2425
     Загрузить в AL байт из ячейки с шестнадцатеричным адресом 0000:0417
2426
                 ES = 0x0000;
2427
                 AL = ESBYTE [0x417];
2428
 
2429
     Переместить слово из ячейки с шестнадцатеричным адресом 2233:4455
2430
     в ячейку с шестнадцатеричным адресом A000:0002
2431
                 $PUSH DS
2432
                 DS = 0x2233;
2433
                 ES = 0xA000;
2434
                 ESWORD [0x0002] = DSWORD [0x4455];
2435
                 $POP DS
2436
 
2437
     Сохранить вычисленное значение выражения X + 2, имеющее
2438
     тип int в ячейке с шестнадцатеричным адресом FFFF:1234
2439
                 ES = 0xFFFF;
2440
                 ESINT [0x1234] = X + 2;
2441
 
2442
     Сохранить BX в сегменте стека по смещению 42:
2443
                 SSWORD [42] = BX;
2444
Return to contents.
2445
 
2446
 
2447
2448
8. Работа с блоками данных.
2449
 
2450
  8.1 Структуры.
2451
 
2452
    8.1.1 Что такое структуры.
2453
2454
 
2455
        Структура позволяет объединить в одном объекте совокупность значений,
2456
    которые могут иметь различные типы.
2457
Return to contents.
2458
 
2459
 
2460
2461
    8.1.2 Синтаксис.
2462
2463
 
2464
    struct [<тег>] { <список-объявлений-элементов> }
2465
      <описатель>[,<описатель>...];
2466
    struct <тег> <описатель> [,<описатель>];
2467
 
2468
        Объявление структуры начинается с ключевого слова struct и имеет две
2469
    формы записи.
2470
 
2471
        В первой форме типы и имена элементов структуры специфицируются в
2472
    списке-объявлений-элементов. Необязательный в данном случае тег - это
2473
    идентификатор, который именует структурный тип, определенный данным
2474
    списком объявлений элементов. описатель специфицирует либо переменную
2475
    структурного типа, либо массив структур данного типа.
2476
 
2477
        Вторая синтаксическая форма объявления использует тег структуры для
2478
    ссылки на структурный тип, определенный где-то в другом месте программы.
2479
 
2480
        Список объявлений элементов представляет собой последовательность из
2481
    одной или более объявлений переменных. Каждая переменная, объявленная в
2482
    этом списке, называется элементом структуры.
2483
 
2484
        Элементы структуры запоминаются в памяти последовательно в том
2485
    порядке, в котором они объявляются. Выравнивание элементов внутри
2486
    структуры по умолчанию не производится. Но существует опция, включение
2487
    которой в командную строку позволяет иметь выравнивание и внутри
2488
    структуры. Сама структура выравнивается на четный адрес если включено
2489
    выравнивание.
2490
 
2491
      Примеры объявлений структур:
2492
 
2493
    struct test
2494
    {
2495
      int a;
2496
      char b[8];
2497
      long c;
2498
    } rr, ff[4];
2499
 
2500
        В этом примере объявлены структура с именем rr и массив из 4 структур
2501
    с именем ff. Всему набору переменных присвоено название (тег) test. Этот
2502
    тег можно использовать для объявления других структур. Например:
2503
 
2504
   struct test dd;
2505
 
2506
        Здесь объявлена структура с именем dd, имеющая набор элементов
2507
    описанных в теге test.
2508
 
2509
        При объявлении структур с ранее объявленным тегом ключевое слово
2510
   struct можно не писать. Т.е можно написать вот так:
2511
 
2512
      test dd;
2513
Return to contents.
2514
 
2515
 
2516
2517
    8.1.3 Инициализация структур при объявлении.
2518
2519
 
2520
        После объявления структуры ее элементы могут принимать произвольные
2521
    значения. Что бы этого не было надо структуры проинициализировать.
2522
    Инициализировать структуры при их объявлении можно только глобальные. C--
2523
    поддерживает несколько способов инициализации структур при их объявлении:
2524
 
2525
      1. Одним значением:
2526
 
2527
       struct test dd=2;
2528
 
2529
    В этом примере всем элементам структуры dd присваивается значение 2.
2530
 
2531
      2. Массивом значений:
2532
 
2533
       struct test dd={1,2,,6};
2534
 
2535
    В этом примере первому элементу структуры dd присваивается значение 1,
2536
    второму - 2, четвертому - 6. Пропущенным и не доинициализированным
2537
    значениям будет присвоено 0 значение.
2538
 
2539
      3. Командой FROM:
2540
 
2541
       struct test dd=FROM "file.dat";
2542
 
2543
    В этом примере на место где расположена структура dd при компиляции будет
2544
    загружено содержимое файла . Если размер файла больше чем размер
2545
    структуры, то лишние байты будут загружены в код программы, но они не
2546
    будут востребованы. Если размер файла меньше чем размер структуры, то
2547
    недостающие байты структуры будут заполнены нулями.
2548
 
2549
      4. Командой EXTRACT:
2550
 
2551
       struct test dd=EXTRACT "file.dat", 24, 10;
2552
 
2553
    В этом примере на место где расположена структура dd при компиляции будет
2554
    загружен фрагмент из файла file.dat длиной 10 байт со смещения 24.
2555
    Недостающие байты будут заполнены нулями.
2556
Return to contents.
2557
 
2558
 
2559
2560
    8.1.4 Инициализация структуры при выполнении программы.
2561
2562
 
2563
        При выполнении программы, кроме присвоения каждому элементу структуры
2564
    значения, можно проинициализировать всю структуру присвоением ей числа или
2565
    переменной. Примеры:
2566
 
2567
    void proc()
2568
    struct test aa[5],rr;
2569
    int i;
2570
    {
2571
      aa[0]=0x12345678;
2572
      aa[i]=int 0x12345678;
2573
      aa=long 0x12345678;
2574
      rr=i;
2575
 
2576
    В первом примере память, занимаемая первой структурой массива из 5
2577
    структур, будет заполнена байтом 0x78 (по умолчанию).
2578
 
2579
    Во втором примере память, занимаемая (i+1)-вой структурой массива из 5
2580
    структур, будет заполнена словом 0x5678.
2581
 
2582
    В третьем примере память, занимаемая всем массивом из 5 структур, будет
2583
    заполнена длинным словом 0x12345678.
2584
 
2585
    В четвертом примере память, занимаемая структурой rr, будет заполнена
2586
    содержимым переменной i.
2587
 
2588
        Можно также копировать содержимое одной структуры в другую. Например:
2589
 
2590
      rr=aa[2];
2591
 
2592
    Будет скопировано содержимое третьей структуры массива структур aa в
2593
    структуру rr.
2594
Return to contents.
2595
 
2596
 
2597
2598
    8.1.5 Операции с элементами структур.
2599
2600
 
2601
        С элементами структур можно выполнять все те операции, которые
2602
    доступны для переменных соответствующего типа. Например:  Объявлена
2603
    структура:
2604
 
2605
    struct test
2606
    {
2607
      int a;
2608
      char b[8];
2609
      long c;
2610
    } rr[3];
2611
    Пример допустимого синтаксиса:
2612
        rr.a = rr.b[i] * rr[1].c + i ;
2613
 
2614
    Примечание:
2615
        При операциях с элементами массива структур и с индексированными
2616
    элементами, в которых в качестве индекса или номера структуры используется
2617
    переменная, компилятор может использовать регистры SI и DI, а в некоторых
2618
    ситуациях (например:  rr[i].b[j] >< rr[i+1].b[j+2] ) будет задействован и
2619
    регистр DX.
2620
 
2621
        Для отдельных элементов структуры, можно получать их адрес, размер
2622
    и смещение в теге структуры. Вот пример:
2623
 
2624
    struct AA       //объявление тега структуры
2625
    {
2626
      word a[3];    // первый элемент структуры
2627
      char b;       // второй элемент структуры
2628
      long c;       // третий элемент структуры
2629
    };
2630
 
2631
    struct BB	//тег второй структуры
2632
    {
2633
      word aa;	// первый элемент
2634
      AA bb;	// второй элемент - вложенная структура
2635
    }ss;		// объявляем структуру с тегом BB
2636
 
2637
    void proc()
2638
    {
2639
      AX=#ss.bb.b; // получить адрес элемента b структуры bb в структуре ss
2640
      AX=#BB.bb.b; // получить смещение этого же элемента в теге BB
2641
      AX=sizeof(ss.bb);	// получить размер элемента bb в структуре ss
2642
      AX=sizeof(BB.bb);	// получить размер элемента bb в теге BB
2643
    }
2644
Return to contents.
2645
 
2646
 
2647
2648
    8.1.6 Вложенные структуры.
2649
2650
 
2651
        При объявлении тегов структур можно использовать теги других,
2652
    объявленных ранее структур. Пример вложенных структур:
2653
 
2654
    struct RGB
2655
    {
2656
      byte Red;
2657
      byte Green;
2658
      byte Blue;
2659
      byte Reserved;
2660
    };
2661
 
2662
    struct BMPINFO
2663
    {
2664
      struct BMPHEADER header; //описание этой структуры пропущено
2665
      struct RGB color[256];
2666
    }info;
2667
 
2668
        Предположим Вам нужно получить содержимое переменной Red десятого
2669
    элемента color. Это можно будет записать так:
2670
 
2671
      AL=info.color[10].Red;
2672
 
2673
        Но существует одно ограничение использования вложенных структур в C--.
2674
    Это невозможность использования переменной в качестве индекса более одного
2675
    раза при обращении к многоэкземплярным структурам. Поясним это на примере:
2676
 
2677
    struct ABC
2678
    {
2679
      int a;
2680
      int b;
2681
      int c;
2682
    };
2683
 
2684
    struct
2685
    {
2686
      struct ABC first[4];  //4 экземпляра структуры ABC
2687
      int d;
2688
    }second[4];
2689
 
2690
    int i,j;
2691
 
2692
    void proc()
2693
    {
2694
      AX=second[i].first[j].a; //такая запись вызовет сообщение об ошибка, так
2695
                               //как переменная использовалась в двух местах
2696
      AX=second[2].first[j].a; //а этот синтаксис допустим.
2697
      AX=second[i].first[3].a;
2698
    }
2699
Return to contents.
2700
 
2701
 
2702
2703
    8.1.7 Отображение тега структуры на блок памяти.
2704
2705
 
2706
        Отображение тега структуры на блок памяти является альтернативой
2707
    указателям на структуры.
2708
 
2709
        Альтернативный способ использования указателей на структуры позволит
2710
    Вам самим выбрать регистр, в котором будет хранится адрес структуры и
2711
    самим следить за его сохранностью и по мере необходимости восстанавливать
2712
    его содержимое.
2713
 
2714
        Объяснить, как использовать отображение тега структуры на память,
2715
    наверное, будет проще на примере:
2716
 
2717
    struct AA       //объявление тега структуры
2718
    {
2719
      word a[3];    // первый элемент структуры
2720
      char b;       // второй элемент структуры
2721
      long c;       // третий элемент структуры
2722
    };
2723
 
2724
    byte buf[256];  //блок памяти, на который будет отображен тег структуры
2725
 
2726
    void proc1()
2727
    {
2728
     ...
2729
     proc2 ( #buf );  // вызов процедуры с передачей ей в качестве параметра
2730
                      // адреса блока памяти
2731
     ...
2732
    }
2733
 
2734
    long proc2 (unsigned int pointer_to_mem)
2735
    {
2736
    int i;
2737
      BX=pointer_to_mem;  // в BX загрузим адрес блока памяти
2738
      FOR(i=0; i<3; i++){ // в массив элемента a записать -1
2739
        BX.AA.a[i]=-1;
2740
      }
2741
      BX.AA.b=0;
2742
      ES:BX.AA.c=EAX;
2743
      return BX.AA.c;  // вернуть содержимое элемента c
2744
    }
2745
 
2746
        В 16-битном режиме для хранения адреса структуры можно использовать
2747
    регистры: BX,DI,SI,BP. Но лучше для этого использовать регистр BX.
2748
    Регистры DI и SI может использовать компилятор при вычислении адреса
2749
    многоэлементных объектов. Регистр BP компилятор использует для работы с
2750
    локальными и параметрическими переменными. В 32-битном режиме можно
2751
    использовать любой кроме ESP и EBP регистр, а регистры EDI и ESI надо
2752
    использовать осторожно.
2753
Return to contents.
2754
 
2755
 
2756
2757
    8.1.8 Битовые поля структур.
2758
2759
 
2760
        Битовые поля структур используются для экономии памяти, поскольку
2761
    позволяют плотно упаковать значения, и для организации удобного доступа к
2762
    регистрам внешних устройств, в которых различные биты могут иметь
2763
    самостоятельное функциональное назначение.
2764
 
2765
        Объявление битового поля имеет следующий синтаксис:
2766
 
2767
    <тип> [<идентификатор>]:<константа>;
2768
 
2769
    или на примере:
2770
 
2771
    int var:5;  //объявление битового поля размером 5 бит с именем var
2772
 
2773
        Битовое поле состоит из некоторого числа битов, которое задается
2774
    числовым выражением константа. Его значение должно быть целым
2775
    положительным числом и его значение не должно превышать числа разрядов,
2776
    соответствующие типу определяемого битового поля. В C-- битовые поля
2777
    могут содержать только без знаковые значения. Нельзя использовать массивы
2778
    битовых полей, указатели на битовые поля.
2779
 
2780
        идентификатор именует битовое поле. Его наличие необязательно.
2781
    Неименованное битовое поле означает пропуск соответствующего числа битов
2782
    перед размещением следующего элемента структуры. Неименованное битовое
2783
    поле, для которого указан нулевой размер, имеет специальное назначение:
2784
    оно гарантирует, что память для следующего битового поля будет начинаться
2785
    на границе того типа, который задан для неименованного битового поля.
2786
    Т.е.  будет произведено выравнивание битового поля на 8/16/32 бита.
2787
 
2788
        В C-- все битовые поля упаковываются одно за другим независимо от
2789
    границ типа идентификаторов. Если последующее поле не является битовым
2790
    полем, то оставшиеся до границы байта биты не будут использованы.
2791
    Максимальный размер битового поля равен 32 бита для типа dword/long, 16
2792
    бит для типа word/int и 8 бит для типа byte/char. Битовые поля можно
2793
    объединять, т.е. использовать их в операторе union. sizeof
2794
    примененный к битовому полю вернет размер этого поля в битах. При
2795
    использовании битового поля, его содержимое будет расширятся в регистр
2796
    как без знаковое целое число.
2797
Return to contents.
2798
 
2799
 
2800
2801
  8.2 Объединения.
2802
2803
 
2804
      Объединения позволяют в разные моменты времени хранить в одном объекте
2805
  значения различного типа.
2806
 
2807
      Память, которая выделяется под объединение, определяется размером
2808
  наиболее длинного из элементов объединения. Все элементы объединения
2809
  размещаются в одной и той же области памяти с одного и того же адреса.
2810
  Значение текущего элемента объединения теряется, когда другому элементу
2811
  объединения присваивается значение.
2812
 
2813
      В C-- реализованы так называемые анонимные объединения. Т.е.
2814
  объединениям не присваивается имя, а обращение к элементам объединения
2815
  происходит как к обычной переменной. Пример:
2816
 
2817
  union
2818
  {
2819
    dword regEAX;
2820
    word  regAX;
2821
    byte  regAL;
2822
  };  // объявили, что 3 переменные расположены по одному и тому же
2823
      // физическому адресу
2824
 
2825
  void test()
2826
  {
2827
  	regEAX = 0x2C;
2828
  	BL = regAL;	//в регистре BL окажется значение 0x2C
2829
  }
2830
 
2831
      Объединять можно переменные различных типов, массивы, строковые
2832
  переменные и структуры. Объединения могут быть глобальными и локальными, а
2833
  также располагаться внутри структур (пока в объединениях внутри структур
2834
  нельзя использовать структуры). Глобальные объединения могут быть
2835
  инициализированными и неинициализированными. Чтобы получить
2836
  инициализированное объединение достаточно проинициализировать лишь первый
2837
  элемент объединения. Если же первый элемент объединения не инициализирован,
2838
  а следующие элементы инициализированы, то это вызовет сообщение компилятора
2839
  об ошибке.
2840
Return to contents.
2841
 
2842
 
2843
2844
  8.3 Команды 'FROM' и 'EXTRACT'.
2845
2846
 
2847
      В C-- есть очень оригинальные команды, которых нет в других языках. Это
2848
  FROM и EXTRACT.
2849
 
2850
      Команда FROM имеет синтаксис:
2851
 
2852
  <тип_переменной> <имя_переменной> = FROM <имя_файла>;
2853
 
2854
      Встретив эту команду при компиляции, компилятор загрузит в выходной
2855
  файл содержимое файла имя_файла, а имя_переменной будет идентификатором
2856
  начала загруженного кода. Вот пример использования этой команды из файла
2857
  tinydraw.c--:
2858
 
2859
  byte palette[PALSIZE] = FROM "TINYDRAW.PAL";  // buffer for palette
2860
 
2861
     Команда EXTRACT имеет синтаксис:
2862
 
2863
  <тип_переменной> <имя_переменной> = EXTRACT <имя_файла>, <начало>, <длина>;
2864
 
2865
      Встретив эту команду при компиляции, компилятор загрузит в выходной
2866
  файл из файла имя_файла число байт равное длина со смещения начало, а
2867
  имя_переменной будет идентификатором начала загруженного кода. Вот пример
2868
  использования этой команды:
2869
 
2870
  byte LIT128 = EXTRACT "8X16.FNT", 16*128, 16;
2871
  byte LIT130 = EXTRACT "8X16.FNT", 16*130, 16;
2872
Return to contents.
2873
 
2874
 
2875
2876
9. Операторы.
2877
 
2878
  9.1 Условные инструкции.
2879
2880
 
2881
      Условные инструкции, при помощи которых осуществляется ветвление, такие
2882
  же как в C.
2883
 
2884
      C-- имеет две инструкции ветвления. if и IF.
2885
 
2886
      if делает близкий условный переход, а IF делает короткий
2887
  (8-разрядный) условный переход. IF выполняется быстрее и может экономить
2888
  до 3 байт в размере кода, но может осуществлять переходы только в пределах
2889
  127 байтов кода.
2890
 
2891
      Условные инструкции, как и в C, могут сопровождаться, как одиночной
2892
  командой, так и блоком из нескольких команд, заключенных в фигурные скобки
2893
  { и }. Условные инструкции имеют те же ограничения, что и условные
2894
  выражения.
2895
 
2896
      Если за инструкцией IF следует больше чем 127 байтов кода, транслятор
2897
  выдаст следующее сообщение об ошибке:
2898
 
2899
          IF jump distance too far, use if.
2900
 
2901
  Это можно просто исправить, заменив в этом месте инструкцию IF на if.
2902
 
2903
      Команды else и ELSE используются точно так же, как в языке C.
2904
  Отличие их в том, что ELSE имеет ограничение адреса перехода 127 байт,
2905
  такое же как IF. else генерирует код на 1 байт длиннее, чем ELSE.
2906
 
2907
      Команды IF и else, а также if и ELSE могут свободно смешиваться
2908
  как в следующем примере:
2909
 
2910
          if( x == 2 )
2911
              WRITESTR("Two");
2912
          ELSE{ WRITESTR("not two.");
2913
                printmorestuff();
2914
              }
2915
 
2916
      Если за инструкцией ELSE следует больше чем 127 байтов кода,
2917
  транслятор выдаст следующее сообщение об ошибке:
2918
 
2919
          ELSE jump distance too far, use else.
2920
 
2921
      Это можно просто исправить, заменив в этом месте инструкцию ELSE на
2922
  else.
2923
Return to contents.
2924
 
2925
 
2926
2927
  9.2 Циклы do{} while.
2928
2929
 
2930
      В таком цикле блок кода, составляющий тело цикла, будет повторяться,
2931
  пока условное выражение имеет значение истинно.
2932
 
2933
      Истинность условного выражения проверяется после выполнения тела  цикла,
2934
  поэтому блок кода будет выполнен, по крайней мере, один раз.
2935
 
2936
   Пример do {} while цикла, в котором тело будет исполнено пять раз:
2937
 
2938
           count = 0;
2939
           do {
2940
              count++;
2941
              WRITEWORD(count);
2942
              WRITELN();
2943
              } while (count < 5);
2944
 
2945
  Условное выражение в do {} while инструкции должно соответствовать тем же
2946
  правилам, что и в инструкциях IF и if.
2947
Return to contents.
2948
 
2949
 
2950
2951
  9.3 Циклы loop, LOOPNZ, loopnz.
2952
2953
 
2954
      Циклы loop повторяют блок кода, пока определенная переменная или
2955
  регистр, выполняющие роль счетчика цикла, содержат значение, отличное от
2956
  нуля. В конце выполнения блока кода, составляющего тело цикла, указанная
2957
  переменная или регистр - уменьшается на 1, а затем проверяется на равенство
2958
  нулю. Если переменная (или регистр) не равна нулю, тело цикла будет
2959
  выполнено снова, и процесс повторится.
2960
 
2961
      Пример использования цикла loop в котором в качестве счетчика цикла
2962
  использована переменная:
2963
 
2964
          count = 5;
2965
          loop( count )
2966
              {WRITEWORD(count);
2967
              WRITELN();
2968
              }
2969
 
2970
      Наибольший эффект дает использование регистра CX для циклов с небольшим
2971
  телом, поскольку в этом случае компилятором генерируется цикл с применением
2972
  машинной команды LOOP.
2973
 
2974
      Если перед стартом счетчик циклов содержит нулевое значение, команды
2975
  тела цикла будут выполнены максимальное число раз для диапазона переменной
2976
  (256 раз для 8-битного счетчика (переменной типа byte или char), 65536 для
2977
  16-битного счетчика (переменной типа word или int), и 4294967296 для
2978
  32-битного счетчика (переменной типа dword или long).
2979
 
2980
   В следующем примере цикл будет выполнен 256 раз:
2981
 
2982
           BH = 0;
2983
           loop (BH)
2984
              {
2985
              }
2986
 
2987
      Если в команде не указано никакого счетчика цикла, цикл будет
2988
  продолжаться бесконечно.
2989
 
2990
      Следующий пример будет непрерывно выводить символ звездочки (*) на
2991
  экран:
2992
 
2993
          loop()
2994
              WRITE('*');
2995
 
2996
      Программист, если хочет, может использовать или изменять значение
2997
  переменной счетчика цикла внутри цикла.
2998
 
2999
      Например, следующий цикл выполнится только 3 раза:
3000
 
3001
           CX = 1000;
3002
           loop( CX )
3003
              {
3004
              IF( CX > 3 )
3005
                  CX = 3;
3006
              }
3007
 
3008
      Цикл можно также прервать оператором разрыва BREAK или break. Вот
3009
  тот же пример с использованием BREAK:
3010
 
3011
           CX = 1000;
3012
           loop( CX )
3013
              {
3014
              IF( CX > 3 )
3015
                  BREAK;
3016
              }
3017
 
3018
      Циклы LOOPNZ/loopnz отличаются от цикла loop, тем, что перед входом
3019
  в цикл проверяется равенство нулю аргумента цикла. Если аргумент равен
3020
  нулю, то тело цикла ни разу не выполнится (в цикле loop в этом случае
3021
  тело цикла выполнится максимальное число раз). Цикл LOOPNZ получается
3022
  максимально эффективным при оптимизации на размер кода, если в качестве
3023
  параметра-счетчика используется регистр CX/ECX. При этом компилятор
3024
  использует ассемблерные инструкции JCXZ/JECXZ и LOOP.
3025
Return to contents.
3026
 
3027
 
3028
3029
  9.4 Цикл while, WHILE.
3030
3031
 
3032
       Синтаксис:
3033
    while(<выражение>)
3034
         <оператор>
3035
 
3036
      Цикл выполняется до тех пор, пока значение выражения не станет
3037
  ложным. Вначале вычисляется выражение. Если выражение изначально ложно,
3038
  то тело оператора while вообще не выполняется и управление сразу
3039
  передается на следующий оператор программы.
3040
 
3041
      Цикл WHILE аналогичен циклу while, но при этом генерируется код на
3042
  3 байта короче. Размер сгенерированного кода в цикле WHILE должен быть
3043
  меньше 127 байт.
3044
 
3045
    Примеры:
3046
  	while ( i < 20 ){
3047
  		WRITEWORD(i);
3048
  		i++;
3049
  	}
3050
 
3051
  	WHILE (i < 20 ) @WRITEWORD(i);	//цикл либо будет бесконечным либо не
3052
                                          //выполнится ни разу
3053
Return to contents.
3054
 
3055
 
3056
3057
  9.5 Цикл for, FOR.
3058
3059
 
3060
       Синтаксис:
3061
    for ([<начальное выражение>]; [<условие>]; [<приращение>])
3062
      <оператор>
3063
 
3064
      Цикл for выполняется до тех пор, пока значение условия не станет
3065
  ложным. Если условие изначально ложно, то тело оператора for вообще не
3066
  выполняется и управление сразу передается на следующий оператор программы.
3067
  Начальное выражение и приращение обычно используются для инициализации
3068
  и модификации параметров цикла.
3069
 
3070
      Первым шагом при выполнении for является вычисление начального
3071
  выражения, если оно имеется. Затем вычисляется условие и производится
3072
  его оценка следующим образом:
3073
 
3074
      1) Если условие истинно, то выполняется тело оператора. Затем
3075
  вычисляется приращение (если оно есть), и процесс повторяется.
3076
 
3077
      2) Если условие опущено, то его значение принимается за истину. В
3078
  этом случае цикл for представляет бесконечный цикл, который может
3079
  завершиться только при выполнении в его теле операторов break, goto,
3080
  return.
3081
 
3082
      3) Если условие ложно, то выполнение цикла for заканчивается и
3083
  управление передается следующему оператору.
3084
 
3085
      Цикл FOR аналогичен циклу for, но при этом генерируется код на 3
3086
  байта короче. Размер сгенерированного кода в цикле FOR должен быть меньше
3087
  127 байт.
3088
 
3089
    Примеры:
3090
  	for(i=0;i<5;i++){
3091
  		WRITESTR("СТРОКА ");
3092
  		WRITEWORD(i);
3093
  		WRITELN();
3094
  	}
3095
 
3096
      Число начальных выражений и число приращений не ограничено. Каждый
3097
  оператор в начальных выражениях и приращениях должен разделяться
3098
  запятой. Пример:
3099
 
3100
        for ( a=1, b=2 ; a<5 ; a++, b+=a ) {...
3101
 
3102
      Также есть возможность логического объединения условий. Объединять
3103
  можно до 32 условий. Каждое объединяемое условие должно быть заключено в
3104
  скобки. Пример:
3105
 
3106
        for ( a=0 ; (a>=0) && (a<10) ; a++ ){...
3107
Return to contents.
3108
 
3109
 
3110
3111
  9.6 Оператор переключатель switch.
3112
3113
 
3114
      Синтаксис:
3115
    switch(<выражение>){
3116
      case <константа>:
3117
        <оператор>
3118
  	...
3119
      case <константа>:
3120
        <оператор>
3121
  	...
3122
      ...
3123
      default:
3124
        <оператор>
3125
    }
3126
      Оператор переключатель switch предназначен для выбора одного из
3127
  нескольких альтернативных путей выполнения программы. Выполнение начинается
3128
  с вычисления значения выражения. После этого управление передается одному
3129
  из операторов тела переключателя. В теле переключателя содержатся
3130
  конструкции: case константа:, которые синтаксически представляют собой
3131
  метки операторов. Оператор, получающий управление, - это тот оператор,
3132
  значение константы которого совпадают со значением выражения
3133
  переключателя. Значение константы должно быть уникальным.
3134
 
3135
      Выполнение тела оператора-переключателя switch начинается с выбранного
3136
  таким образом оператора, и продолжается до конца тела или до тех пор, пока
3137
  какой-либо оператор не передаст управление за пределы тела.
3138
 
3139
    Оператор, следующий за ключевым словом default, выполняется, если ни
3140
  одна из констант не равна значению выражения. Если default опущено, то
3141
  ни один оператор в теле переключателя не выполняется, и управление
3142
  передается на оператор, следующий за switch.
3143
 
3144
      Для выхода из тела переключателя обычно используется оператор разрыва
3145
  break (BREAK).
3146
 
3147
    Пример:
3148
    switch (i){
3149
      case 'A':
3150
        WRITE(i);
3151
        i++;
3152
        BREAK;
3153
      case 32:
3154
        WRITE('_');
3155
        i++;
3156
        BREAK;
3157
      default:
3158
        WRITE('i');
3159
    }
3160
 
3161
      Оператор switch сейчас в компиляторе может реализовываться трем
3162
  способами: двухтабличным, табличным и методом последовательных проверок.
3163
 
3164
      Табличный метод является самым быстрым, а при большом числе операторов
3165
  case и при незначительной разнице между максимальным и минимальным
3166
  значениями case он еще может быть и более компактным. Но у него есть и
3167
  недостатки: в 16-битном режиме компилятор всегда использует регистр BX, а в
3168
  32-битном режиме, если операндом switch является регистр, то его значение
3169
  будет разрушено.
3170
 
3171
      В методе последовательных проверок блок сравнений находится в начале
3172
  тела оператора switch, это позволяет избавиться от 1-2 лишних jmp. Но
3173
  компилятор не может определить, какой тип перехода использовать при
3174
  проверке значений case. Это будет Вашей заботой. Если размер кода от
3175
  начала тела оператора switch до места расположения оператора case
3176
  меньше 128 байт, можно использовать короткий переход. В этом случае Вы
3177
  можете указать оператор CASE, что приведет к генерации более компактного
3178
  кода. Компилятор в предупреждениях будет Вам подсказывать о возможности
3179
  использования операторов CASE. Использование оператора CASE в случаях,
3180
  когда размер блока кода более 128 байт приведет к выдаче компилятором
3181
  сообщения об ошибке.
3182
 
3183
     При двухтабличном методе создаются две таблицы - таблица адресов входа в
3184
  тело оператора switch/SWITCH и таблица значений case. Генерируется
3185
  процедура сравнения входного значения со значениями во второй таблице. Если
3186
  есть совпадение, то делается переход по адресу из второй таблицы. Этот
3187
  метод является самым медленным, но при большом числе значений case (более
3188
  15) он становится самым компактным.
3189
 
3190
      При оптимизации кода на размер, компилятор предварительно вычисляет
3191
  размер кода, который может быть получен всеми методами и реализует самый
3192
  компактный. При оптимизации на скорость преимущество отдается табличному
3193
  методу, если размер таблицы получается не слишком большим.
3194
 
3195
      Для оператора switch введена также и короткая его форма - SWITCH.
3196
  Ее можно применять в случае, если размер блока кода между началом тела
3197
  оператора и оператором default (если он отсутствует, то концом тела
3198
  оператора switch) меньше 128 байт. О возможности использования короткой
3199
  формы компилятор будет сообщать в предупреждениях.
3200
 
3201
      Для оператора case/CASE, который может использоваться только в теле
3202
  блока оператора switch/SWITCH, можно указывать диапазон значений. Сначала
3203
  надо указывать меньшее значение, затем после многоточия большее. Пример:
3204
 
3205
  switch(AX){
3206
    case 1...5:
3207
      WRITESTR("Range AX from 1 to 5");
3208
      BREAK;
3209
  };
3210
 
3211
      Раньше Вам бы пришлось писать более громоздкую конструкцию:
3212
 
3213
  switch(AX){
3214
    case 1:
3215
    case 2:
3216
    case 3:
3217
    case 4:
3218
    case 5:
3219
      WRITESTR("Range AX from 1 to 5");
3220
      BREAK;
3221
  };
3222
 
3223
      Кроме того, что новый формат записи более компактен и более читабелен,
3224
  но еще при этом компилятор создает более компактный и быстрый код.
3225
Return to contents.
3226
 
3227
 
3228
3229
  9.7 Оператор перехода goto, GOTO.
3230
3231
 
3232
       Синтаксис:
3233
       goto <метка>;
3234
  	.
3235
  	.
3236
  	.
3237
  <метка>:
3238
 
3239
      Оператор перехода goto передает управление на оператор помеченный
3240
  меткой. Аналогом в ассемблере оператору goto является команда jmp near.
3241
  Аналогом в ассемблере оператору GOTO является команда jmp short.
3242
Return to contents.
3243
 
3244
 
3245
3246
  9.8 Оператор разрыва break, BREAK.
3247
3248
 
3249
      Оператор разрыва break прерывает выполнение операторов do-while,
3250
  for, switch, while, loop, loopnz, LOOPNZ. Он может содержаться
3251
  только в теле этих операторов. Управление передается оператору, следующему
3252
  за прерванным циклом.
3253
 
3254
      Оператор BREAK аналогичен break, но при этом генерируется код на 1
3255
  байт короче. Размер сгенерированного кода от места где применяется BREAK
3256
  до конца цикла должен быть меньше 127 байт.
3257
 
3258
    Примеры:
3259
  	FOR (i=0; ; i++){
3260
  		FOR(j=0; j < WIDTH; j++){
3261
  			IF(i==5)BREAK;
3262
  		}
3263
  		IF(i==10)BREAK;
3264
  	}
3265
Return to contents.
3266
 
3267
 
3268
3269
  9.9 Оператор продолжения continue, CONTINUE.
3270
3271
 
3272
      Оператор продолжения continue передает управление на следующую
3273
  итерацию в циклах do-while, for, while, loop, loopnz. В циклах
3274
  do-while, while, loop следующая итерация начинается с вычисления
3275
  условного выражения. Для цикла for следующая итерация начинается с
3276
  вычисления выражения приращения, а затем происходит вычисление условного
3277
  выражения.
3278
 
3279
    Оператор CONTINUE аналогичен continue, но при этом генерируется код на
3280
  1 байт короче. Размер сгенерированного кода от места где применяется
3281
  CONTINUE до начала итерации должен быть меньше 127 байт.
3282
Return to contents.
3283
 
3284
 
3285
3286
  9.10 Логическое объединение условий.
3287
3288
 
3289
      Существует возможность логического объединения сравнений в условиях
3290
  IF и if, циклах do{}while, while{}, WHILE{}, for{} и FOR{}.
3291
  Каждое сравнение при их логическом объединении должно быть заключено в
3292
  скобки.  Объединять можно не более 32 сравнений.
3293
 
3294
      В отличие от C в C-- анализ логических объединений происходит слева
3295
  направо и все лишние скобки будут восприняты компилятором как ошибочные.
3296
  Это несколько снижает гибкость и возможности применения этих объединений,
3297
  но такова традиция и философия, заложенная в C--.
3298
 
3299
      Пример:
3300
 
3301
  	   if ( (a>3) && (b>4) || (c<8) ){
3302
 
3303
  Т.е. если произвести расшифровку этого условия, то получится следующее:
3304
  условие выполнится если a>3 и b>4 или a>3 и c<8.
3305
Return to contents.
3306
 
3307
 
3308
3309
  9.11 Переход через циклы.
3310
3311
 
3312
     Для операторов BREAK, break, CONTINUE, continue введена
3313
  поддержка числового параметра, определяющего, сколько циклов надо
3314
  пропустить, прежде чем будет выполнен этот оператор. Например, мы имеем три
3315
  вложенных цикла:
3316
 
3317
  do{
3318
     loop(CX){
3319
        for(BX=0;BX<10;BX++){
3320
  	 break;	  //стандартный оператор
3321
  	 break 0; //break с параметром - пропустить 0 циклов
3322
  	 break 1; //break с параметром - пропустить 1 цикл
3323
  	 break 2; //break с параметром - пропустить 2 цикла
3324
        }
3325
  LABL0:
3326
     }
3327
  LABL1:
3328
  }while (DX!=0);
3329
  LABL2:
3330
 
3331
      В третьем цикле находится группа различных вариантов оператора break.
3332
  Первым стоит стандартный оператор break, при выполнении которого
3333
  управление будет передаваться за пределы третьего цикла - на метку LABL0.
3334
  Вторым идет оператор break 0, при выполнении которого будет пропущено 0
3335
  циклов и управление будет передано опять же на метку LABL0. Таким
3336
  образом, запись break и break 0 являются синонимами. Третьим идет
3337
  оператор break 1, при выполнении которого будет пропущен один цикл и
3338
  управление будет передано за пределы второго цикла на метку LABL1. Ну и
3339
  наконец, последним идет оператор break 2, при выполнении которого
3340
  компилятор пропустит два цикла и передаст управление за пределы третьего,
3341
  на метку LABL2. Метки в этом примере проставлены для удобства объяснения.
3342
  Ну и я надеюсь, Вам понятно, что значение параметра не может превышать
3343
  числа циклов находящихся перед текущим. Так для одиночного цикла этот
3344
  параметр может принимать максимальное и единственное значение - 0.
3345
Return to contents.
3346
 
3347
 
3348
3349
  9.12 Инвертирование флага проверки условий.
3350
3351
 
3352
      Инвертирование флага проверки условий в операциях сравнения if/IF
3353
  for/FOR while/WHILE происходит с помощью символа ! - not.
3354
 
3355
     Выражени
3356
 
3357
    IF ( NOTCARRYFLAG )...   и  IF ( ! CARRYFLAG )...
3358
    IF ( proc() == 0 )...    и IF ( ! proc() ) ...
3359
 
3360
  являются синонимами.
3361
Return to contents.
3362
 
3363
 
3364
3365
  9.13 Вычисление выражения, а затем проверка условия.
3366
3367
 
3368
      В операциях сравнения в левом операнде теперь допустимо использовать
3369
  вычисления выражения с присваиванием и операции инкремента, декремента.
3370
  Например:
3371
 
3372
    IF (i=a+2 != 0 )...
3373
    IF ( i++ )...
3374
    IF ( a-- )...
3375
    IF ( i+=4 == 0 )...
3376
 
3377
      Во всех этих примерах сначала произойдет вычисление выражения в левой
3378
  части операции сравнения, а потом будет произведено сравнение результата с
3379
  правой частью выражения сравнения.
3380
Return to contents.
3381
 
3382
 
3383
3384
  9.14 Проверка битов при операции сравнения.
3385
3386
 
3387
      Если в левой части выражения сравнения написано: BX & 5, то при
3388
  вычислении выражения содержимое регистра BX будет изменено инструкцией
3389
  and. Но иногда возникает необходимость в проверке битов без изменения
3390
  содержимого регистра BX. Для этих целей надо использовать инструкцию
3391
  test. Как же указать компилятору, в каких ситуациях использовать
3392
  инструкцию and, а в каких test? В стандартных языках C для этого
3393
  используется механизм приоритетов - если выражение заключено в скобки, то
3394
  производится его вычисление, если нет, то производится проверка. Но C-- не
3395
  поддерживает приоритетов. Для разрешения этой проблемы в C-- решено
3396
  использовать непосредственно саму инструкцию test. Вот допустимые
3397
  варианты синтаксиса:
3398
 
3399
  IF ( $test AX,5 )
3400
  IF ( ! $test AX,5)
3401
  IF ( asm test AX,5)
3402
  IF ( ! asm { test AX,5 } )
3403
Return to contents.
3404
 
3405
 
3406
3407
  9.15 Оператор перестановки.
3408
3409
 
3410
      В C-- есть оператор, который не встречается в других языках, это
3411
  оператор перестановки. Оператор перестановки меняет местами содержимое двух
3412
  переменных. Символьное обозначение этого оператора ><. Переменные с обеих
3413
  сторон оператора перестановки должны иметь одинаковый размер, 8 бит и 8
3414
  бит, 16 бит и 16 бит, или 32 бита и 32 бита.
3415
 
3416
    Вот некоторые примеры:
3417
 
3418
      AX >< BX; // сохраняет значение BX в AX и значение AX в BX
3419
      CH >< BL; // меняет местами содержимое регистров CH и BL
3420
      dog >< cat; /* меняет местами значения переменной dog и переменной cat*/
3421
      counter >< CX; // меняет местами значения переменной counter
3422
                     // и содержимое регистра CX
3423
 
3424
      Если перестановка осуществляется между двумя 8-разрядными переменными в
3425
  памяти, будет разрушено содержимое регистра AL. Если перестановка - между
3426
  двумя 16-разрядными переменными в памяти, будет разрушено содержимое
3427
  регистра AX. Если перестановка - между двумя 32-разрядными переменными в
3428
  памяти, будет разрушено содержимое EAX. В любом другом случае, например,
3429
  между переменной в памяти и регистром, значения всех регистров будут
3430
  сохранены.
3431
Return to contents.
3432
 
3433
 
3434
3435
  9.16 Оператор отрицания.
3436
3437
 
3438
      C-- поддерживает быстрый синтаксис смены знака переменной - оператор
3439
  отрицания. Поставив - (знак минус) перед идентификатором переменной памяти
3440
  или регистра и ; (точку с запятой) после идентификатора, вы смените знак
3441
  переменной памяти или регистра.
3442
 
3443
  Вот некоторые примеры:
3444
 
3445
           -AX; // результат тот же, что и при 'AX = -AX;' ,но быстрее.
3446
           -tree; // то же самое, что 'tree = -tree;' ,но быстрее.
3447
           -BH; // меняет знак BH.
3448
Return to contents.
3449
 
3450
 
3451
3452
  9.17 Оператор инверсии.
3453
3454
 
3455
      C-- поддерживает быстрый синтаксис выполнения логической инверсии
3456
  значения переменной - оператор инверсии. Поставив ! (восклицательный знак)
3457
  перед идентификатором переменной памяти или регистром и ; (точку с
3458
  запятой) после идентификатора, вы выполните логическую (выполнится
3459
  ассемблерная команда NOT) инверсию текущего значения переменной. Вот
3460
  некоторые примеры:
3461
 
3462
          !AX; // то же самое, что ' AX ^ = 0xFFFF; ' но быстрее.
3463
          !node; // заменяет значение 'node' его логической инверсией.
3464
          !CL; // то же самое, что ' CL ^ = 0xFF ' но быстрее.
3465
Return to contents.
3466
 
3467
 
3468
3469
  9.18 Специальные условные выражения.
3470
3471
 
3472
      C-- поддерживает восемь специальных условных выражений:
3473
 
3474
           CARRYFLAG
3475
           NOTCARRYFLAG
3476
           OVERFLOW
3477
           NOTOVERFLOW
3478
           ZEROFLAG
3479
           NOTZEROFLAG
3480
           MINUSFLAG
3481
           PLUSFLAG
3482
 
3483
      Они могут использоваться вместо любых нормальных условных выражений.
3484
  Если Вы желаете, например, выполнить блок кода только если установлен флаг
3485
  переноса, Вам следует использовать следующую последовательность команд:
3486
 
3487
           IF( CARRYFLAG )
3488
           {
3489
           // здесь вы чего-то делаете
3490
           }
3491
 
3492
      Если Вы желаете непрерывно выполнять блок кода до тех пор, пока не
3493
  установится флаг переполнения, Вам следует использовать нечто подобное
3494
  следующему куску кода:
3495
 
3496
          do {
3497
              // здесь вы опять чего-то делаете
3498
              } while( NOTOVERFLOW );
3499
Return to contents.
3500
 
3501
 
3502
3503
  9.19 Символ $ - вставляет текущий адрес программы.
3504
3505
 
3506
      Символ $, кроме того, что является признаком последующей ассемблерной
3507
  инструкции, в языке C--, как и в языке Assembler может указывать текущий
3508
  адрес (смещение) компилируемой программы. Но в C-- он имел ограниченные
3509
  возможности. Он мог быть использован лишь как аргумент в операторах
3510
  GOTO/goto и ассемблерных инструкциях DW/DD/JMP.
3511
 
3512
      Этот символ может находиться в любом месте вычисляемого числового
3513
  выражения и может быть применен в любом месте совместно с другими числовыми
3514
  выражениями.
3515
 
3516
  Примеры применения:
3517
 
3518
  DW #main-$	//записать расстояние от процедуры main до текущего места
3519
  GOTO $+2;	//перейти по адресу на 2 больше, чем текущий адрес
3520
Return to contents.
3521
 
3522
 
3523
3524
  9.20 Ключевое слово static и оператор ::.
3525
3526
 
3527
      Если перед объявлением глобальной переменной, структуры или процедуры
3528
  указать слово static, то эти переменная, структура или процедура будут
3529
  доступны только в том файле, в котором они были объявлены. Т.е. если Вы
3530
  включите этот файл в другой директивой include, то переменные объявленные
3531
  во включаемом файле со словом static не будут доступны в основном файле,
3532
  и Вы можете в основном файле объявить другие переменные с такими же
3533
  именами.
3534
 
3535
      Если Вы примените слово static при объявлении локальной переменной в
3536
  процедуре, то память для этой переменной будет выделена не в стеке, а в
3537
  области данных процедуры. Но эта переменная будет доступна только внутри
3538
  процедуры, в которой она была объявлена. Применение static к локальным
3539
  переменным дает возможность сохранять значение переменной для следующего
3540
  входа в процедуру.
3541
 
3542
      Слово static можно применять к любому глобальному объекту
3543
  (переменной, структуре, процедуре). Для локального использования это слово
3544
  можно применять только к переменным.
3545
 
3546
      Если в Вашей программе есть глобальная и локальная переменная с
3547
  одинаковыми именами, то в процедуре, в которой объявлена эта локальная
3548
  переменная, Вы не имели доступа к одноименной глобальной переменной.
3549
  Применив перед именем переменной оператор ::, Вы получите доступ к
3550
  глобальной переменной.  Пример:
3551
 
3552
  int var;  //объявляем глобальную переменную
3553
 
3554
  void proc()
3555
  int var;  //объявляем локальную переменную с именем уже существующей
3556
            //глобальной переменной
3557
  {
3558
    (E)AX=var;	//имеем доступ только к локальной переменной
3559
    (E)AX=::var;  //а так можно получить доступ к глобальной переменной
3560
  }
3561
Return to contents.
3562
 
3563
 
3564
3565
  9.21 Оператор sizeof.
3566
3567
 
3568
      Операция sizeof определяет размер памяти, который соответствует объекту
3569
  или типу. Операция sizeof имеет следующий вид:
3570
 
3571
   sizeof (<имя типа>)
3572
 
3573
      Результатом операции sizeof является размер памяти в байтах,
3574
  соответствующий заданному объекту или типу.
3575
 
3576
      В C-- оператор sizeof можно применять к переменным, регистрам, типам
3577
  переменных, структурам, процедурам, текстовым строкам и файлам.
3578
 
3579
      Если операция sizeof применяется к типу структуры, то результатом
3580
  является размер тега данной структуры.
3581
 
3582
      Если операция sizeof применяется к текстовой строке, то результатом
3583
  операции является размер строки плюс завершающий нуль. Например:
3584
 
3585
   sizeof ("Test")
3586
 
3587
  результатом этой операции будет число 5. Если Вы напишите такую
3588
  конструкцию:
3589
 
3590
  char a="Test";
3591
 
3592
   sizeof(a)
3593
 
3594
  то результатом будет 5 - размер памяти, отведенный для переменной a.
3595
 
3596
      При использовании оператора sizeof с именем структуры вставляет
3597
  фактический размер памяти, занимаемый структурой. Это особенно важно, если
3598
  Вы объявили массив структур.
3599
 
3600
      Оператор sizeof можно применять и к имени определенной ранее
3601
  процедуры. Результатом будет размер этой процедуры. Но для динамических
3602
  процедур всегда будет ноль.
3603
 
3604
      Операцию sizeof можно применять и к файлам. Это бывает очень полезным
3605
  при использовании оператора FROM, но может применяться и в других случаях.
3606
  Пример применения оператора sizeof к файлам:
3607
 
3608
   sizeof ( file "filename.dat" )
3609
 
3610
  Результатом этой операции будет размер файла "filename.dat".
3611
Return to contents.
3612
 
3613
 
3614
3615
  9.22 Метки перехода.
3616
3617
 
3618
      Метки перехода применяются для указания начальных точек участков кода,
3619
  используемых командами перехода встроенного ассемблера и операторами
3620
  goto/GOTO.
3621
 
3622
      Имеются два типа меток перехода: глобальные и локальные. Глобальные
3623
  метки, как следует из названия, это метки, которые видимы из любого места в
3624
  программе. Локальные метки видны только в пределах своего процедурного
3625
  блока, и не определены за его пределами.
3626
 
3627
      Метки определяются идентификатором, оканчивающимися двоеточием. Если
3628
  идентификатор содержит хотя бы один символ строчных букв (букв нижнего
3629
  регистра, маленьких букв), это глобальная метка перехода, в противном
3630
  случае, это локальная метка перехода.
3631
 
3632
      Глобальные метки перехода не должны использоваться внутри динамических
3633
  процедур; там можно использовать только локальные метки. Это важно помнить,
3634
  поскольку, из-за применения такого средства как макрокоманды, динамическая
3635
  процедура может присутствовать в нескольких местах кода, что будет
3636
  означать, что метке соответствует больше чем один адрес.
3637
 
3638
      Метки вне процедур фактически располагаются в области данных программы.
3639
  Если данные и код находятся в одном сегменте (а именно так организованна
3640
  программа, написанная на C--), то метки вне процедур становятся простым и
3641
  эффективным методом для получения расстояний между частями программы. В
3642
  качестве имен для меток вне процедур могут быть использованы уникальные
3643
  идентификаторы, в которых можно использовать большие, маленькие и смесь
3644
  больших и маленьких букв.
3645
Return to contents.
3646
 
3647
 
3648
3649
10. Ассемблер.
3650
 
3651
  10.1 Поддержка команд ассемблера.
3652
3653
 
3654
      Встроенный в C-- ассемблер поддерживает все инструкции 8088/8086,
3655
  80286, 80386, 80486, Pentium, Pentium II и Pentium III процессоров.
3656
 
3657
      Все инструкции встроенного ассемблера должны начинаться с символа
3658
  доллара $. Поддерживается также ключевое слово asm, которое являясь
3659
  синонимом к символу доллара, еще и поддерживает объединение ассемблерных
3660
  инструкций в блоки.
3661
Return to contents.
3662
 
3663
 
3664
3665
  10.2 Ключевое слово asm.
3666
3667
 
3668
      Ключевое слово asm является синонимом к $ - префикс ассемблерной
3669
  команды. После слова asm можно писать блок ассемблерных команд.  Пример:
3670
 
3671
  	asm {
3672
  		.
3673
  		.
3674
  		push AX
3675
  labl:
3676
  		push BX
3677
  		mov AX,0x1234
3678
  		jmp short labl
3679
  		.
3680
  		.
3681
  		.
3682
  	}
3683
 
3684
    Метки внутри блока ассемблерных команд допустимы.
3685
Return to contents.
3686
 
3687
 
3688
3689
  10.3 Префикс dup - повторение инструкций DB/DW/DD.
3690
3691
 
3692
      Для ассемблерных инструкции DB, DW, DD введена возможность использовать
3693
  префикс повторений dup. Применение этого префикса имеет следующий
3694
  синтаксис:
3695
 
3696
    $DW NUMREP dup VALTOREP
3697
 
3698
  NUMREP - число повторов инструкции DW.
3699
  VALTOREP - величина, которая будет повторена NUMREP раз.
3700
 
3701
      В отличие от аналога этого префикса из ассемблера повторяемую величину
3702
  заключать в скобки нельзя.
3703
Return to contents.
3704
 
3705
 
3706
3707
  10.4 Инструкции процессора Pentium III.
3708
3709
 
3710
      В компилятор добавлена поддержка 19 новых инструкций MMX расширения
3711
 
3712
  MASKMOVQ   mmx,mmx
3713
  MOVNTQ     m64,mmx
3714
  PAVGB      mmx,mmx/m64
3715
  PAVGW      mmx,mmx/m64
3716
  PEXTRW     r32,mmx,i8
3717
  PINSRW     mmx,r32/m16,i8
3718
  PMAXUB     mmx,mmx/m64
3719
  PMAXSW     mmx,mmx/m64
3720
  PMINUB     mmx,mmx/m64
3721
  PMINSW     mmx,mmx/m64
3722
  PMOVMSKB   r32,mmx
3723
  PMULHUW    mmx,mmx/m64
3724
  PREFETCHT0 mem
3725
  PREFETCHT1 mem
3726
  PREFETCHT2 mem
3727
  PREFETCHNTA mem
3728
  SFENCE
3729
  PSADBW     mmx,mmx/m64
3730
  PSHUFW     mmx,mmx/m64,i8
3731
 
3732
      и 46 инструкций SSE расширения.
3733
 
3734
  ADDPS      xmm,m128/xmm
3735
  ADDSS      xmm,xmm/m32
3736
  ANDNPS     xmm,xmm/m128
3737
  ANDPS      xmm,xmm/m128
3738
  COMISS     xmm,xmm/m32
3739
  DIVPS      xmm,m128/xmm
3740
  DIVSS      xmm,xmm/m32
3741
  MAXPS      xmm,m128/xmm
3742
  MAXSS      xmm,xmm/m32
3743
  MINPS      xmm,m128/xmm
3744
  MINSS      xmm,xmm/m32
3745
  MULPS      xmm,m128/xmm
3746
  MULSS      xmm,xmm/m32
3747
  ORPS       xmm,xmm/m128
3748
  RCPPS      xmm,xmm/m128
3749
  RCPSS      xmm,xmm/m32
3750
  RSQRTPS    xmm,xmm/m128
3751
  RSQRTSS    xmm,xmm/m32
3752
  SQRTPS     xmm,m128/xmm
3753
  SQRTSS     xmm,xmm/m32
3754
  SUBPS      xmm,m128/xmm
3755
  SUBSS      xmm,xmm/m32
3756
  UCOMISS    xmm,xmm/m32
3757
  UNPCKHPS   xmm,xmm/m128
3758
  UNPCKLPS   xmm,xmm/m128
3759
  XORPS      xmm,xmm/m128
3760
  CMPPS      xmm,xmm/m128,i8
3761
  CMPSS      xmm,xmm/m32,i8
3762
  SHUFPS     xmm,xmm/m128,i8
3763
  CVTPI2PS   xmm,m64/mmx
3764
  CVTSI2SS   xmm,m32/r32
3765
  CVTPS2PI   mmx,m128/xmm
3766
  CVTTPS2PI  mmx,xmm/m128
3767
  CVTSS2SI   r32,xmm/m128
3768
  CVTTSS2SI  r32,xmm/m128
3769
  LDMXCSR    m32
3770
  STMXCSR    m32
3771
  MOVHLPS    xmm,xmm
3772
  MOVLHPS    xmm,xmm
3773
  MOVMSKPS   r32,xmm
3774
  MOVNTPS    m128,xmm
3775
  MOVAPS     m128/xmm,xmm/m128
3776
  MOVSS      xmm/m32,xmm/m32
3777
  MOVUPS     xmm/m128,m128/xmm
3778
  MOVHPS     xmm/m64,m64/xmm
3779
  MOVLPS     xmm/m64,m64/xmm
3780
 
3781
      Многие из этих инструкций могут использовать в качестве операнда
3782
  64-битные и 128-битные ячейки памяти. Компилятор C-- сейчас может работать
3783
  только с 32-битными переменными. Поэтому для инструкций использующих в
3784
  качестве операнда ячейки памяти размером больше 32-бит можно использовать
3785
  переменные любых типов. Компилятор не будет выдавать на это сообщений об
3786
  ошибке, будет использован адрес этой переменной, а сама инструкция будет
3787
  использовать нужное ей число битов памяти, начиная с адреса указанной
3788
  переменной. Например:
3789
 
3790
      Для инструкции movaps один из операндов может быть 128-битной
3791
  ячейкой памяти. Для этой инструкции допустимы следующий синтаксис:
3792
 
3793
  byte  var8_128[16];
3794
  word  var16_128[8];
3795
  dword var32_128[4];
3796
 
3797
  void proc()
3798
  {
3799
  asm{
3800
    movaps var8_128,xmm0 //в массив из 16 байт будет записано содержимое XMM0
3801
    movaps xmm1,var16_128	//в XMM1 будет записано содержимое 8 слов
3802
    movaps var32_128,xmm1 //в массив из 4 двойных слов будет записано XMM1
3803
  }
3804
  }
3805
Return to contents.
3806
 
3807
 
3808
3809
11. Процедуры.
3810
 
3811
  11.1 Типы процедур, функций и макрокоманд.
3812
3813
 
3814
      Сейчас C-- поддерживает 4 типа вызова процедур: cdecl, pascal, stdcall
3815
  и fastcall. Вот краткие характеристики этих типов вызовов процедур:
3816
 
3817
  cdecl  Этот тип вызова процедур является по умолчанию для языка С. Он
3818
  характеризуется тем, что параметры процедуры передаются в порядке обратном
3819
  их записи. Очистка стека от параметров производится после завершения работы
3820
  процедуры. Этот способ вызова процедур очень удобен для процедур с
3821
  переменным числом параметров.
3822
 
3823
  pascal  Этот тип вызова предполагает, что параметры передаются в том
3824
  порядке, в котором они записаны в программе. Освобождение стека от
3825
  параметров производит сама вызываемая процедура. Этот тип вызова является
3826
  более компактным, чем cdecl.
3827
 
3828
  stdcall  Этот тип вызова является гибридом первых двух. Параметры
3829
  передаются процедуре в порядке обратном, тому в котором они записаны в
3830
  программе. Освобождение стека от параметров производится в самой вызываемой
3831
  процедуре.
3832
 
3833
  fastcall  Этот тип вызова процедур предполагает что передача параметров
3834
  процедуре производится через регистры, тем самым отпадает необходимость
3835
  освобождения стека от параметров. Для этого типа вызова процедуры
3836
  существуют ограничения по числу передаваемых параметров. Для C это три
3837
  параметра, а для C-- шесть. В C-- параметры передаются по умолчанию в
3838
  следующем порядке: 1-й - AX/EAX, 2-й - BX/EBX, 3 - CX/ECX, 4 - DX/EDX, 5 -
3839
  DI/EDI, 6 - SI/ESI. Параметры типов char или byte могут передаваться в
3840
  количестве не более 4 или только в первых 4 регистрах: 1 - AL, 2 - BL, 3 -
3841
  CL, 4 - DL. Этот порядок регистров может быть изменен, если явно указать
3842
  его либо при объявлении процедуры, либо при ее определении. Процедуры типа
3843
  fastcall иногда еще называют регистровыми.
3844
 
3845
      В C-- по умолчанию, если имя процедуры написано большими буквами, то
3846
  считается, что эта процедура имеет тип вызова fastcall. Если же в имени
3847
  процедуры есть хотя бы одна маленькая буква, то по умолчанию считается, что
3848
  эта процедура имеет тип вызова pascal, за исключением программ
3849
  компилируемых с ключом /w32 /w32c или /DLL. В них по умолчанию применяется
3850
  тип вызова процедур stdcall. Если же Вы хотите изменить тип вызова процедур
3851
  из по умолчанию на любой другой, то эту процедуру надо обязательно объявить
3852
  с указанием типа желаемого вызова.
3853
 
3854
      Объявление процедур введено для того, чтобы сообщать компилятору о
3855
  типе возврата из процедур, способе передачи параметров процедуре и их числе.
3856
Return to contents.
3857
 
3858
 
3859
3860
  11.2 Стековые процедуры.
3861
3862
 
3863
      Стековые процедуры по умолчанию объявляются при помощи идентификатора,
3864
  который содержит, по крайней мере, один символ строчных букв (букв нижнего
3865
  регистра, маленьких букв). Таким образом, стековые процедуры легко отличимы
3866
  от регистровых процедур, поскольку для имен регистровых процедур символы
3867
  строчных букв запрещены.
3868
 
3869
      Параметры для стековых процедур, если они есть, могут иметь любой тип
3870
  byte, char, word, int, dword, long или float.
3871
 
3872
      Параметры передаются в соответствии с правилами, принятыми для данного
3873
  типа процедур. Если процедура не имеет объявления, то компилятор не следит
3874
  за числом и типом передаваемых параметров. В этом случае у Вас появляется
3875
  свобода в их использовании, но Вы должны осознавать и последстви
3876
  неправильного их использования.
3877
 
3878
      В списке параметров для каждого параметра указывается его тип.
3879
  Параметры одного типа, идущие подряд, разделяются запятыми. Формальные
3880
  параметры разного типа в объявлении функции разделяются символом ;.
3881
 
3882
      В следующем примере стековая процедура возвращает сумму всех своих
3883
  параметров (имеющих различные типы) как величину типа word:
3884
 
3885
  	word add_them_all (int a,b,c; byte d,e; word x,y)
3886
  	{
3887
  	return( a+b+c+d+e+x+y );
3888
  	}
3889
 
3890
      Ранее C-- делал вызовы стековых процедур лишь в стиле pascal.
3891
  Преимуществом этого способа вызова процедур является компактность и более
3892
  простой механизм генерации кода. К недостаткам, а соответственно и
3893
  преимуществам С-стиля, можно отнести жесткую привязанность паскалевских
3894
  процедур к числу и типу передаваемых параметров (попробуйте при вызове
3895
  процедуры в стиле pascal опустить один параметр и получите 100% зависание).
3896
  Напомню некоторые технические детали обоих типов вызовов процедур.
3897
 
3898
  Кадр стека C-- для близких процедур стека в стиле pascal:
3899
       АДРЕС
3900
        ...
3901
      BP + FFFE предпоследний байта локальных переменных
3902
      BP + FFFF последний байт локальных переменных
3903
      BP + 0000 Сохраненный BP
3904
      BP + 0002 RET адрес
3905
      BP + 0004 последнее слово передаваемых процедуре параметров (если они
3906
                есть)
3907
      BP + 0006 предпоследнее слово передаваемых процедуре параметров
3908
       ...
3909
      BP + nnnn первое слово передаваемых процедуре параметров
3910
 
3911
      Освобождение стека от переданных процедуре параметров происходит прямо
3912
  в самой процедуре командой RET nnnn - где nnnn является размером переданных
3913
  в стек параметров.
3914
 
3915
  Кадр стека C-- для близких процедур стека в стиле си:
3916
       АДРЕС
3917
        ...
3918
      BP + FFFE предпоследний байта локальных переменных
3919
      BP + FFFF последний байт локальных переменных
3920
      BP + 0000 Сохраненный BP
3921
      BP + 0002 RET адрес
3922
      BP + 0004 первое слово передаваемых процедуре параметров (если они
3923
                есть)
3924
      BP + 0006 второе слово передаваемых процедуре параметров
3925
       ...
3926
      BP + nnnn последнее слово передаваемых процедуре параметров
3927
 
3928
      Процедуры в стиле С заканчиваются командой RET. Освобождение стека от
3929
  параметров происходит в том месте откуда была вызвана процедура. Обычно это
3930
  делается командой ADD SP,nnnn. Т.е. компилятор может точно знать сколько и
3931
  каких параметров Вы передаете в данном случае процедуре и соответственно
3932
  освобождает стек после завершения процедуры. Это очень удобно для процедур,
3933
  которые могут обрабатывать переменное число параметров (например, процедуры
3934
  типа printf).
3935
 
3936
      Объявление процедуры имеет следующий вид:
3937
 
3938
    rettype modif procname();
3939
 
3940
      Первым идет необязательный тип возврата из процедур. По умолчанию он
3941
  для 16-битных программ равен word, а для 32-битных dword. Затем должен идти
3942
  также необязательный модификатор. По умолчанию все стековые процедуры в C--
3943
  (за исключением режима компиляции программ под Windows, где по умолчанию
3944
  действует стиль вызова процедур stdcall) имеют стиль pascal. Далее идет им
3945
  процедуры со скобками, которые являются признаком того что Вы объявляете
3946
  процедуру, а не переменную. Завершает объявление символ точка с запятой.
3947
 
3948
      При объявлении процедур в C-- прописывать параметры процедуры
3949
  необязательно (тогда компилятор не будет контролировать число и тип
3950
  передаваемых параметров), но если Вы их вставите, то включится механизм
3951
  контроля за числом и типом параметров.
3952
Return to contents.
3953
 
3954
 
3955
3956
  11.3 Регистровые процедуры.
3957
3958
 
3959
      Регистровые процедуры определяются, по умолчанию, при помощи
3960
  идентификатора, который не содержит символов строчных букв. Или же явным
3961
  указанием что это регистровая процедура с помощью ключевого слова fastcall.
3962
 
3963
      Как уже было сказано, параметры (если они есть) для регистровой
3964
  процедуры передаются через регистры. Регистровые процедуры могут иметь не
3965
  более 6 параметров. Если параметры имеют тип int или word, регистры по
3966
  умолчанию используются в следующем порядке: AX, BX, CX, DX, DI, и SI.
3967
  Первые четыре параметра могут также иметь тип char или byte, в этом случае
3968
  задействуются регистры AL, BL, CL и DL соответственно. Любой из шести
3969
  параметров может иметь тип long, dword или float, тогда для него
3970
  используется регистр EAX, EBX, ECX, EDX, EDI, или ESI.
3971
 
3972
      В следующем примере регистровая процедура с именем TOGETHER возвращает
3973
  значение типа word как результат умножения первого параметра, имеющего тип
3974
  word, на второй параметр того же типа:
3975
 
3976
           word TOGETHER() /* AX = первый параметр, BX = второй параметр */
3977
           {
3978
           return (AX * BX);
3979
           }
3980
 
3981
      В следующем примере регистровая процедура с именем SHOW_NUM, которая не
3982
  возвращает никакого значения, зато выводит на экран первый параметр
3983
  (имеющий тип int), затем разделительный знак в виде двоеточия :, а затем
3984
  второй параметр (имеющий тип byte) :
3985
 
3986
           void SHOW_NUM () /* AX = первое число, BL = второе число */
3987
           {
3988
           $ PUSH BX
3989
           WRITEINT (int AX);
3990
           WRITE (':');
3991
           $ POP BX
3992
           WRITEWORD (BL);
3993
           }
3994
 
3995
      Но если в процедуре сделать объявление порядка и типов используемых
3996
  регистров, то возможно произвольное использование регистров. Более подробно
3997
  об этом можно почитать в разделе об объявлениях параметров в регистровых
3998
  процедурах.
3999
 
4000
      Для того, чтобы использовать регистровую процедуру как макрокоманду,
4001
  она должна быть объявлена как динамическая процедура. Динамические
4002
  процедуры описаны в следующем подразделе.
4003
Return to contents.
4004
 
4005
 
4006
4007
  11.4 Динамические процедуры.
4008
4009
 
4010
      Динамические процедуры - процедуры, которые определены, но вставляются
4011
  в код программы, только если есть вызов. Динамические процедуры могут
4012
  использоваться как макрокоманды.
4013
 
4014
      Определение динамической процедуры начинается с символа двоеточия ':'.
4015
 
4016
  Пример динамической процедуры стека:
4017
 
4018
          : void setvideomode (byte mode)
4019
          {
4020
          AL = mode;
4021
          AH = 0;
4022
          $ INT 0x10
4023
          }
4024
 
4025
  Пример динамической регистровой процедуры:
4026
 
4027
          : int ABS () /* AX = число, абсолютное значение которого ищется*/
4028
          {
4029
          IF (int AX < 0)
4030
               -AX;
4031
          }
4032
Return to contents.
4033
 
4034
 
4035
4036
    11.4.1 Установка динамической процедуры в определенное место программы.
4037
4038
 
4039
        Динамические процедуры, если они не используются как макросы и если
4040
    они были востребованы в программе, вставляются в код программы в самом
4041
    конце компиляции. В каком точно месте Вашей программы они окажутся узнать
4042
    невозможно. Если же Вам необходимо, чтобы какая-то динамическая процедура
4043
    находилась в конкретном месте программы, то это можно сделать таким
4044
    образом:
4045
 
4046
    :void proc ( int par1, par2)
4047
    {
4048
       ...
4049
    }
4050
 
4051
        Мы имеем динамическую процедуру, код которой был бы расположен ранее
4052
    кода обычной процедуры нашей программы. Для этого перед определением этой
4053
    процедуры надо написать такую строку:
4054
 
4055
    @ void proc ();
4056
 
4057
	В итоге динамическая процедура будет вставлена в код программы не в
4058
    конце ее, как обычно, а в месте, где будет расположена эта строка. Если
4059
    динамическая процедура имеет параметры, то прописывать эти параметры
4060
    необязательно.
4061
 
4062
	В компиляторе есть еще более мощное средство, позволяющее все
4063
    динамические объекты ( процедуры, переменные, структуры ) расположить в
4064
    указанном месте, а не в конце программы, как обычно. Это директива
4065
    #setdinproc. Встретив эту директиву, компилятор немедленно расположит все
4066
    известные ему на этот момент динамические объекты в месте объявления этой
4067
    директивы. Последующие динамические объекты будут располагаться как
4068
    обычно, в конце программы, если конечно, не будет повторно применена
4069
    директива #setdinproc.
4070
 
4071
	Это может быть применено и быть полезным при создании резидентных
4072
    программ (TSR) и драйверов устройств.
4073
Return to contents.
4074
 
4075
 
4076
4077
  11.5 inline-процедуры.
4078
4079
 
4080
      inline-процедурами могут быть динамические процедуры, которые можно
4081
  использовать как макросы. Но в отличие от макросов, inline-процедуры, при
4082
  включенной оптимизации на скорость, автоматически вставляются в код, а при
4083
  оптимизации кода на размер, делается вызов их, как динамических процедур.
4084
 
4085
      Но иногда бывает нужно при включенной оптимизации на размер кода, чтобы
4086
  процедуры вставлялись в код, а не делался их вызов. Для этих целей введена
4087
  директива #inline TRUE. Этой же директивой ( #inline FALSE ), можно при
4088
  оптимизации на скорость делать вызовы процедур, вместо их вставки.
4089
 
4090
      Важно помнить, что статус директивы #inline автоматически меняется при
4091
  смене режима оптимизации. При установке оптимизации на скорость статус
4092
  директивы #inline устанавливается в TRUE, а при смене режима оптимизации по
4093
  размеру кода, устанавливается в FALSE. Поэтому применяйте директиву #inline
4094
  лишь после смены режима оптимизации.
4095
 
4096
      Директивы меняющие режим оптимизации #codesize, #speed и директива
4097
  #inline, объявленные внутри процедуры распространяются только на оставшуюся
4098
  часть процедуры, т.е. они становятся локальными. Для того чтобы изменения
4099
  были глобальными эти директивы надо объявлять вне тела процедуры.
4100
 
4101
      Для того чтобы определить inline-процедуру, надо в первой строке с
4102
  именем процедуры вместо символа динамической процедуры (:) написать
4103
  ключевое слово inline. Пример определения inline-процедуры:
4104
 
4105
  inline int fastcall abs(AX)
4106
  {
4107
      IF ( int AX < 0 ) -AX ;
4108
  }
4109
Return to contents.
4110
 
4111
 
4112
4113
    11.5.1 Другое применение inline.
4114
4115
 
4116
	Ключевое слово inline имеет в процедурах и другое применение. Если
4117
    это слово расположено перед началом блока процедуры, то для такой
4118
    процедуры не создается кадр стека и не генерируется завершающий процедуру
4119
    ret. Пример:
4120
 
4121
    void PROC ()
4122
    inline
4123
    {
4124
      ...
4125
    }
4126
 
4127
	Такие процедуры не должны содержать локальных переменных. Если
4128
    процедура является регистровой (тип fastcall), то с передачей ей
4129
    параметров нет проблем. Если же процедура является стековой, то передать
4130
    в такую процедуру параметры Вы можете, но воспользоваться этими
4131
    параметрами используя их имена, Вы уже не сможете. Это происходит потому,
4132
    что в этих процедурах кадр стека не формируется. Пример:
4133
 
4134
    void proc (int par1, par2)
4135
    inline
4136
    {
4137
      AX=par1; /* компилятор обратится с параметру 'par1' через регистр BP.
4138
		  Но так как кадр стека не был создан, при выполнении этого
4139
                  кода программа будет работать не правильно. */
4140
       ...
4141
    }
4142
 
4143
	Встретив такое определение процедуры, компилятор выдаст предупреждение
4144
    о том, что в таких процедурах использовать локальные и параметрические
4145
    переменные нельзя.
4146
Return to contents.
4147
 
4148
 
4149
4150
  11.6 Процедуры обработки прерываний.
4151
4152
 
4153
      Процедуры обработки прерываний определяются следующим способом:
4154
 
4155
          interrupt procedure_name ()
4156
          {
4157
          // put code here (здесь должен быть код обработки)
4158
          }
4159
 
4160
      Процедуры обработки прерываний не сохраняют никаких регистров
4161
  автоматически, и никакие регистры сами по себе не загружаются перед
4162
  передачей управления обработчику прерывания, следовательно, на Вашей
4163
  совести сохранение значений регистров в стеке и последующий их возврат, а
4164
  также загрузка регистра DS нужным значением.
4165
 
4166
      Вот пример обработчика прерывания, который сохраняет значения всех
4167
  регистров и загружает регистр DS:
4168
 
4169
       interrupt safe_handle ()
4170
       {
4171
       $ PUSH DS
4172
       $ PUSH ES
4173
       $ PUSHA   // для выполнения этой команды нужен процессор не хуже 80286
4174
       DS = CS;  // здесь DS загружается для работы с моделью памяти типа tiny
4175
 
4176
 
4177
       /* do your thing here (здесь вы делаете свою обработку)*/
4178
 
4179
       $ POPA   // для выполнения этой команды нужен процессор не хуже 80286
4180
       $ POP ES
4181
       $ POP DS
4182
       }
4183
 
4184
      При завершении процедуры прерывания будет автоматически сгенерирована
4185
  инструкция выхода из обработчика прерывания - IRET.
4186
Return to contents.
4187
 
4188
 
4189
4190
  11.7 Замена return на goto.
4191
4192
 
4193
      В некоторых ситуациях, при компиляции программы, оператор return
4194
  будет заменяться на goto. Это происходит при разрешенной оптимизации по
4195
  размеру кода для операторов return, которые расположены внутри процедуры
4196
  и, естественно, если размер кода для выполнения return больше, чем размер
4197
  кода для реализации goto. Для динамических процедур, которые используются
4198
  как макросы, такая замена будет производится всегда. Оператор goto будет
4199
  выполнен на конец процедуры, там, где будет располагаться единственный
4200
  выход из процедуры. В динамических процедурах, используемых в качестве
4201
  макросов, return в конце процедуры будет пропущен компилятором.
4202
 
4203
      Таким образом, снято последнее ограничение на использование
4204
  динамических процедур в качестве макросов. Любая динамическая процедура
4205
  может быть использована как макрос.
4206
 
4207
      Для оператора goto существует его более короткий аналог - GOTO.
4208
  Для получения более компактного кода для оператора return введен также
4209
  более короткий оператор RETURN. Его можно использовать, если от места
4210
  его применения до конца процедуры находится не более 128 байт. Если Вы
4211
  будете использовать RETURN на большем расстоянии до конца процедуры, то
4212
  компилятор выдаст сообщение об ошибке. При использовании return на
4213
  расстоянии меньше 128 байт до конца кода, компилятор выдаст вам
4214
  предупреждение о возможном использовании RETURN.
4215
Return to contents.
4216
 
4217
 
4218
4219
  11.8 Возвращаемые значения.
4220
4221
 
4222
      Возвращаемые из функций значения располагаются в регистрах. В таблице
4223
  показано, какой регистр используется для каждого из возвращаемых типов:
4224
 
4225
      --------------------------------------------
4226
      | возвращаемый тип |  используемый регистр |
4227
      --------------------------------------------
4228
      |        byte      |        AL             |
4229
      |        word      |        AX             |
4230
      |        dword     |        EAX            |
4231
      |        char      |        AL             |
4232
      |        int       |        AX             |
4233
      |        long      |        EAX            |
4234
      |        float     |        EAX            |
4235
      --------------------------------------------
4236
 
4237
      Самый простой способ вернуть значение из функции состоит в том, чтобы
4238
  использовать команду return(), но вместо этого можно напрямую загрузить
4239
  возвращаемое значение в соответствующий регистр. Например, следующие две
4240
  функции возвращают одно и то же значение:
4241
 
4242
           byte proc_one ()
4243
           {
4244
           return (42);
4245
           }
4246
 
4247
           byte proc_two ()
4248
           {
4249
           AL = 42;
4250
           }
4251
 
4252
      Многие DOS функции 0x21 прерывания в качестве индикатора успешного
4253
  выполнения используют установку или сброс carry флага. Использовать флаги
4254
  процессора при возврате из процедур можно и в других случаях, когда надо
4255
  иметь статус успешного или не успешного выполнения процедуры. Это позволит
4256
  более полно использовать возможности процессора и соответственно уменьшит
4257
  размер кода и повысит быстродействие программы.
4258
 
4259
      Наряду с флагами, при возврате из процедур, по прежнему остается
4260
  возврат различных типов и через регистр AL/AX/EAX. Если для процедуры
4261
  объявлено, что она имеет тип возврата int и CARRYFLAG, то при использовании
4262
  такой процедуры в операциях сравнения IF, WHILE... будет делаться проверка
4263
  carry флага, а не сравнение регистра AX. Пример использования возврата
4264
  флагов из процедур:
4265
 
4266
  int CARRYFLAG FOPEN();	// объявление процедуры
4267
 
4268
  void proc()
4269
  {
4270
    IF ( FOPEN(name,0) ) Error ( "Not open file" );
4271
  }
4272
 
4273
      Варианты допустимого синтаксиса для использования возврата флага:
4274
 
4275
  IF ( ! FOPEN() )...
4276
  IF ( @ FOPEN() )...
4277
  IF ( ! @ FOPEN() )...
4278
  IF ( handl = FOPEN() )...
4279
  IF ( handl = @ FOPEN() )...
4280
  IF ( ! handl = FOPEN() )...
4281
  IF ( ! handl = @ FOPEN() )...
4282
 
4283
      А вот варианты, в которых, несмотря на то, что для процедуры объявлен
4284
  возврат флага, будет производиться сравнение регистра AX:
4285
 
4286
  IF ( FOPEN() == 5 )...	// производится сравнение
4287
  IF ( FOPEN() + 2 )...   // результат процедуры подвергается дальнейшему
4288
                          // вычислению, в результате которого флаги будут
4289
  			// изменены.
4290
Return to contents.
4291
 
4292
 
4293
4294
  11.9 Объявление параметров в регистровых процедурах.
4295
4296
 
4297
      Ранее каждому параметру регистровой процедуры соответствовал строго
4298
  определенный регистр. Например, для переменных типа int или word первый
4299
  параметр передавался через регистр AX, 2-й - BX, 3-й - CX, 4-й - DX, 5-й -
4300
  DI, 6-й - SI. Поэтому, если Вам было необходимо передать только один
4301
  параметр через регистр SI, то приходилось перед ним писать пять запятых.
4302
  Вот как, например, выглядит вызов процедуры STRCPY:
4303
 
4304
  void main ()
4305
  {
4306
    STRCPY ( , , , , #dest, #sourc ) ;
4307
  }
4308
 
4309
      Теперь регистры могут располагаться при передаче параметров
4310
  произвольным образом. Надо только объявить компилятору о том, какой регистр
4311
  закреплен за каким параметром данной процедуры. После такого объявления
4312
  компилятор будет сам следить за тем, через какой регистр передавать
4313
  параметр процедуре, его размерностью и числом передаваемых параметров. Вот
4314
  как будет выглядеть объявление и использование процедуры STRCPY:
4315
 
4316
  void STRCPY ( DI, SI ) ;	//это объявление процедуры
4317
 
4318
  void main ()
4319
  {
4320
    STRCPY ( #dest, #sourc ) ;	//а это вызов процедуры
4321
  }
4322
 
4323
      Можно не делать объявления процедуры, а указать расположение регистров
4324
  в заголовке процедуры. Но тогда такая процедура должна вызываться только
4325
  после ее определения. Вот пример процедуры выводящей на экран несколько
4326
  одинаковых символов:
4327
 
4328
  void PUTNCHAR(AL,CX,BL,BH)
4329
  /* 1 параметр в AL - код символа, который будет выведен
4330
     2 параметр в CX - число выводимых символов
4331
     3 параметр в BL - цветовой атрибут
4332
     4 параметр в BH - номер видеостраницы
4333
  */
4334
  {
4335
    AH=9;
4336
    $INT 0x10
4337
  }
4338
 
4339
      При объявлении регистровой процедуры можно также указывать какой тип
4340
  переменной ожидает процедура (знаковый/без знаковый или вещественный). По
4341
  умолчанию считается без знаковый тип. Однако знаковый тип указывать есть
4342
  смысл только если параметр передается через регистр AL/AX/EAX. Через другие
4343
  регистры переменная всегда передается как без знаковая. Пример объявления
4344
  регистровой процедуры с указанием типов:
4345
 
4346
  int fastcall Exampl( word CX, int AX, DX, float ESI ) ;
4347
   |    |        |        |         |   |   |
4348
   |    |        |        |         |   |   |---- 4-й парам. имеет тип float и
4349
   |    |        |        |         |   |         перед. через регистр ESI.
4350
   |    |        |        |         |   |-------- 3-й парам. имеет по умолч.
4351
   |    |        |        |         |             тип word и перед. через DX.
4352
   |    |        |        |         |------------ 2-й парам. имеет тип int и
4353
   |    |        |        |                       передается через регистр AX.
4354
   |    |        |        |---------------------- 1-й парам. имеет тип word и
4355
   |    |        |                                передается через регистр CX.
4356
   |    |        |------------------------------- Имя объявляемой процедуры.
4357
   |    |---------------------------------------- Модификатор, указывающий, что
4358
   |                                              эта проц. явл. регистровой.
4359
   |--------------------------------------------- Процедура возвращает перемен.
4360
  						                          типа int.
4361
 
4362
      Если Вы сделали объявление регистров процедуры, то компилятор будет
4363
  строго следить за количеством указанных параметров при вызове этой
4364
  процедуры и выдавать сообщения об ошибке, если их будет меньше или больше.
4365
  С одной стороны это хорошо - есть контроль за тем, что Вы ничего не забыли
4366
  или не добавили лишнего при вызове процедуры. С другой стороны иногда
4367
  бывают необязательные параметры, а их теперь придется прописывать. Но если
4368
  Вы при вызове процедуры не укажете ни одного параметра, то компилятор не
4369
  будет Вам выдавать сообщение об ошибке.  Это дает Вам возможность
4370
  проинициализировать регистры, через которые Вы передаете параметры, вне
4371
  вызова процедуры.  Но если Вы укажете, хоть один параметр, то Вам придется
4372
  указывать и остальные, иначе компилятор будет считать, что Вы их случайно
4373
  пропустили и выдаст сообщение об ошибке.
4374
 
4375
      Если Вы не объявили регистры ни при объявлении регистровой процедуры,
4376
  ни в заголовке самой процедуры, то компилятор будет считать, что параметры
4377
  в эту процедуру передаются старым способом. Таким образом, достигается
4378
  полная совместимость с предыдущими версиями компилятора.
4379
Return to contents.
4380
 
4381
 
4382
4383
  11.10 Объявление параметров в стековых процедурах.
4384
4385
 
4386
      Как известно, ранее в C-- контроль за числом и типом передаваемых
4387
  процедуре параметров возлагался на программиста. Поэтому возникла непростая
4388
  задача, совместить одновременно отсутствие контроля за параметрами (для
4389
  совместимости с предыдущими версиями) и ее наличие. В результате
4390
  компромиссов появился вариант немного отличающийся от традиционно принятого
4391
  в языках C.
4392
 
4393
      Главное отличие - это то, что параметры, определяемые при определении
4394
  процедуры, не будут восприниматься компилятором для контроля за ними. Во
4395
  всех языках C допускается совмещение прототипа процедуры и ее объявления.
4396
  В C-- для того, чтобы включился контроль за параметрами стековой процедуры,
4397
  надо эту процедуру обязательно объявить. Но не всякое объявление процедуры
4398
  будет сигналом компилятору о включении контроля за параметрами этой
4399
  процедуры. Если при объявлении в круглых скобках ничего не будет, то
4400
  компилятор не будет отслеживать параметры, передаваемые этой процедуре. В
4401
  C++ такое объявление означает, что процедуре не передаются никакие
4402
  параметры. В C-- для этого надо при объявлении процедуры в круглых скобках
4403
  обязательно написать void. Например:
4404
 
4405
  int proc ( void ) ;
4406
 
4407
      Встретив такое объявление процедуры, компилятор будет следить за тем,
4408
  чтобы этой процедуре не были переданы параметры.
4409
 
4410
      При объявлении процедуры имена параметров можно опускать. Как известно,
4411
  в C-- параметры процедуры одного типа записываются через запятую. Для смены
4412
  типа используют точку с запятой. При объявлении смену типа можно
4413
  производить и после запятой:
4414
 
4415
  void ptoc ( int a, b, c; word d );
4416
  void proc ( int, int, int, word );
4417
  void proc ( int, int, int; word );
4418
 
4419
      Все эти примеры объявлений являются идентичными и допустимыми.
4420
 
4421
      Для контроля за процедурами с переменным числом параметров был введен
4422
  новый для C-- элемент синтаксиса - многоточие или его еще называют эллипс.
4423
  Вот как будет выглядеть объявление процедуры printf:
4424
 
4425
  void cdecl printf ( word, ... );
4426
Return to contents.
4427
 
4428
 
4429
4430
  11.11 Использование макрокоманд.
4431
4432
 
4433
      Теперь любая динамическая процедура может быть использована как макрос.
4434
  Если перед вызовом динамической процедуры поставить символ @, то код этой
4435
  процедуры будет вставлен, а не вызван инструкцией CALL.
4436
 
4437
      При использовании стековых динамических процедур в качестве макросов
4438
  очистка стека от переданных параметров производится ассемблерной
4439
  инструкцией ADD SP,SIZE_PARAMETRS сразу после окончания кода вставленного
4440
  макроса. Поэтому, если эта процедура использовала флаги в качестве
4441
  возврата, то они будут разрушены.
4442
Return to contents.
4443
 
4444
 
4445
4446
  11.12 Передача параметров в стековые процедуры через регистры.
4447
4448
 
4449
      При передаче параметров через регистры, чаще всего получается более
4450
  компактный и быстрый код. Но содержимое регистров может быть легко
4451
  разрушено. Если в Вашей процедуре, какой-то из параметров используется
4452
  однократно для того, чтобы в начале процедуры инициализировать какой-то
4453
  регистр, то Вы можете передать это значение в процедуру сразу через
4454
  регистр, минуя стадию засовывания и извлечения содержимого в стек. Пример:
4455
 
4456
  int proc (int param1, param2, param3)
4457
  {
4458
    (E)BX = param3;
4459
    (E)BX.TEG_STRUCT.var = proc2 (param1,papra2);
4460
    proc3 (param1,param2);
4461
  }
4462
 
4463
      В этом примере параметр param3 используется лишь для того, чтобы
4464
  инициализировать регистр (E)BX, поэтому его можно сразу передать через
4465
  регистр:
4466
 
4467
  int proc (int param1, param2, (E)BX)
4468
  {
4469
    (E)BX.TEG_STRUCT.var = proc2 (param1,papra2);
4470
    proc3 (param1,param2);
4471
  }
4472
 
4473
      Как Вы видите, процедура немного упростилась.
4474
 
4475
      В принципе, порядок расположения стековых и регистровых параметров не
4476
  принципиален. Но надо помнить, что содержимое регистров может быть легко
4477
  разрушено, и поэтому лучше всего регистровые параметры инициализировать
4478
  лишь после того, как были засунуты в стек все стековые параметры. Для
4479
  процедур типа pascal регистровые параметры лучше располагать после
4480
  стековых параметров. Для процедур типа cdecl и stdcall сначала лучше
4481
  располагать регистровые параметры.
4482
Return to contents.
4483
 
4484
 
4485
4486
  11.13 Вызов процедур с адресом в регистре.
4487
4488
 
4489
      В C-- допустимо делать вызов процедуры, адрес которой находится в
4490
  регистре. Параметры для такого вызова передаются только через стек. Тип
4491
  вызова процедуры для программ под Windows stdcall, для остальных pascal.
4492
  Адрес процедуры для 32-битных программ должен находится в 32-битном
4493
  регистре, а для 16-битных программ в 16-битном регистре. Считается, что
4494
  такой вызов имеет возврат типа unsigned int. Пример:
4495
 
4496
    BX = # proc;
4497
    BX (a);
4498
    IF ( BX(b) == 0 ) AX=2;
4499
 
4500
    Вы получите следующий код:
4501
 
4502
  test.c-- 8: BX=#proc;
4503
  0104 BB1A01                   mov     bx,11Ah
4504
 
4505
  test.c-- 9: BX(a);
4506
  0107 FF76FC                   push    word ptr [bp-4]
4507
  010A FFD3                     call    near bx
4508
 
4509
  test.c-- 10: IF (BX(b) == 0)AX=2;
4510
  010C FF76FE                   push    word ptr [bp-2]
4511
  010F FFD3                     call    near bx
4512
  0111 85C0                     test    ax,ax
4513
  0113 7503                     jne     118h
4514
  0115 B80200                   mov     ax,2
4515
Return to contents.
4516
 
4517
 
4518
4519
  11.14 Встроенные в компилятор процедуры.
4520
4521
 
4522
      Для некоторых процедур Вы не найдете их исходные тексты в библиотеках
4523
  компилятора. Код этих процедур генерирует компилятор. Вот список этих
4524
  процедур:
4525
 
4526
  ABORT             Прекращение выполнения программы
4527
  atan              Вычислить арктангенс числа
4528
  atan2             Вычислить арктангенс числа
4529
  ATEXIT            Зарегистрировать функцию выполняющуюся при выходе.
4530
  cos               Возвращает косинус угла
4531
  EXIT              Закончить программу с кодом ошибки
4532
  exp               Возвращает экспоненту числа
4533
  inp/inportb       Считать один байт из порта
4534
  inport            Считать слово из порта
4535
  inportd           Считать двойное слово из порта
4536
  fabs              Возвращает абсолютное значение числа
4537
  log               Вычисляет натуральный логарифм числа
4538
  log10             Вычисляет десятичный логарифм числа
4539
  outp/outportb     Записать один байт в порт
4540
  outport           Записать слово в порт
4541
  outportd          Записать двойное слово в порт
4542
  sin               Возвращает синус угла
4543
  sqrt              Извлечь квадратный корень через FPU.
4544
  tan               Возвращает тангенс угла
4545
 
4546
      Размещение этих процедур непосредственно в компиляторе, связано с тем,
4547
  что в настоящий момент компилятор может таким образом генерировать более
4548
  эффективный код, чем если бы эти процедуры располагались в библиотеках.
4549
  В будущем, по мере развития компилятора, эти процедуры постепенно будут
4550
  выносится из компилятора в библиотеки.
4551
 
4552
      Но ничто не мешает Вам уже сейчас написать свои одноименные
4553
  библиотечные процедуры. Встретив определение такой процедуры, компилятор не
4554
  будет выдавать никаких сообщение, он просто будет применять Ваш вариант
4555
  процедуры.
4556
Return to contents.
4557
 
4558
 
4559
4560
    11.14.1 Процедуры ABORT, ATEXIT и EXIT.
4561
4562
 
4563
        Процедуры ABORT и EXIT связаны с работой директивы #atexit и
4564
    процедурой ATEXIT. Наиболее оптимальную их реализацию и взаимную
4565
    интеграцию может сделать только компилятор. Именно поэтому эти процедуры
4566
    поддерживаются компилятором.
4567
 
4568
        Процедура ATEXIT - регистровая процедура, которая регистрирует
4569
    функцию, адрес которой передается ей в качестве параметра, т.е. через
4570
    регистр (E)AX, как функцию завершения программы. При успешной регистрации
4571
    ATEXIT возвращает 0. Всего можно зарегистрировать до 16 функций.
4572
 
4573
        Завершающие функции не должны иметь параметров и возврата. Эти
4574
    функции будут выполняться в порядке обратном очередности регистрации в
4575
    случае, если Вы будете завершать работу программы через вызовы процедур
4576
    ABORT или EXIT или закончится работа процедуры main. Если Вы
4577
    завершите работу программы вызовом процедуры ExitProcess под Windows или
4578
    вызовом AH=0x4C; $int 0x21 под DOS, выход из программы произойдет без
4579
    запуска зарегистрированных функций.
4580
 
4581
        Процедура ABORT и EXIT, если не включена директива #atexit делают
4582
    вызов процедуры ExitProcess под Windows и вызов AH=0x4C; $int 0x21 под
4583
    DOS.  Процедуре ABORT не передаются никакие параметры, и она завершает
4584
    работу программы с кодом возврата 0. Процедуре EXIT передается в
4585
    качестве параметра код возврата, с которым она и завершает работу
4586
    программы.
4587
Return to contents.
4588
 
4589
 
4590
4591
    11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и
4592
                                                                    outportd
4593
4594
 
4595
        Эти процедуры всегда вставляются в код как макросы, т.е. для этих
4596
    процедур никогда не генерируется вызов процедуры. В зависимости от
4597
    значения порта, с которым работают эти процедуры, генерируется разный
4598
    код. Все это позволяет получать более компактный код.
4599
 
4600
        Процедуры чтения из порта имеют такой прототип:
4601
 
4602
    byte inp ( word port );
4603
    word inport ( word port );
4604
    dword inportd ( word port );
4605
 
4606
        Процедуры записи в порт имеют такой прототип:
4607
 
4608
    void outp ( byte val; word port );
4609
    void outport ( word val; word port );
4610
    void outportd ( dword val; word port );
4611
 
4612
        Имена процедур inp и inportb, также как и имена outp и outportb
4613
    являются синонимами.
4614
Return to contents.
4615
 
4616
 
4617
4618
    11.14.3 Процедуры для работы с вещественными числами.
4619
4620
 
4621
        Эти процедуры реализуются компилятором и всегда вставляются в код как
4622
    макросы, т.е. для них никогда не генерируется вызов процедуры. Кроме
4623
    этого, если параметром одной процедуры является вызов другой, то
4624
    результат работы второй процедуры остается в стеке FPU, а первая
4625
    процедура использует этот результат непосредственно из стека. Таким
4626
    образом получаются более компактный код. Вот вымышленный пример:
4627
 
4628
    test.c-- 7: f = sin( sqrt(1) );
4629
    0100 D9061C01                 fld     [11Ch]
4630
    0104 D9FA                     fsqrt
4631
    0106 D9FE                     fsin
4632
    0108 D91E2001                 fstp    [120h]
4633
    010C 9B                       fwait
4634
 
4635
        Эти процедуры имеют следующий прототип:
4636
 
4637
    float atan ( float val );
4638
    float atan ( float val, val2 );
4639
    float cos ( float val );
4640
    float exp ( float val );
4641
    float fabs ( float val );
4642
    float log ( float val );
4643
    float log10 ( float val );
4644
    float sin ( float val );
4645
    float sqrt ( float val );
4646
    float tan ( float val );
4647
Return to contents.
4648
 
4649
 
4650
4651
  11.15 Классы.
4652
 
4653
    11.15.1 Объявление процедур в структурах.
4654
4655
 
4656
        С введение поддержки объявления процедур в структурах, структура
4657
    становится подобной классу в C++. Т.е. такая процедура становится методом
4658
    класса. Пример:
4659
 
4660
    struct Point  // объявление класса
4661
    {
4662
    	int x; // элементы данных
4663
    	int y; // класса типа Point
4664
    	void SetX(int);  // объявление методов
4665
    	void SetY(int);  // класса Point
4666
    };
4667
 
4668
    void Point::SetX(int _x)  //определение процедуры класса Point
4669
    {
4670
    	IF((_x>=0)&&(_x<=MAX_X)) x=_x;
4671
    // переменные x, y являются членами этого класса и поэтому доступ к ним из
4672
    // процедур этого же класса осуществляется напрямую.
4673
     }
4674
 
4675
    void main()
4676
    Point p;  //определяем структуру в стеке
4677
    {
4678
      p.y = p.x = 0;
4679
      p.SetX(1);
4680
    }
4681
 
4682
        При вызове процедуры являющейся методом класса ей неявным образом
4683
    передается адрес этого класса (структуры). В самой процедуре этот адрес
4684
    доступен через имя параметрической переменной this. Эту переменную
4685
    автоматически генерирует компилятор. Если в объявление процедуры в
4686
    структуре указать ключевое слово static, то такой процедуре адрес
4687
    класса не передается и переменная this не генерируется.
4688
 
4689
        Процедура объявленная в структуре может быть динамической. Для этого,
4690
    при ее определении, в самом ее начале, надо написать символ двоеточия :
4691
    (также как и для обычных динамических процедур). Но такая динамическая
4692
    процедура не может быть использована как макрос.
4693
Return to contents.
4694
 
4695
 
4696
4697
    11.15.2 Наследование.
4698
4699
 
4700
        В C-- поддерживаются простые и множественные наследования. Объявление
4701
    структуры с наследованием имеет следующий синтаксис:
4702
 
4703
    struct Derived : Base1, Base2, ... Basen
4704
    {
4705
      int x0;
4706
    };
4707
 
4708
        Число базовых структур в производном не ограничено. При множественном
4709
    наследовании структура может наследовать два и более экземпляра базовой
4710
    структуры. При этом возникает неоднозначность. Пример:
4711
 
4712
    struct A
4713
    {
4714
      int x,y;
4715
      . . .
4716
    };
4717
 
4718
    struct B : A  //структура B наследует A
4719
    {
4720
      . . .
4721
 
4722
    };
4723
 
4724
    struct C : A  //структура C наследует A
4725
    {
4726
      . . .
4727
    };
4728
 
4729
    struct D : B, C //структура D наследует B и C
4730
    {
4731
      . . .
4732
    };
4733
 
4734
    void main()
4735
    D d;  //выделяем для структуры D память в стеке и присваиваем ей имя d
4736
    {
4737
      d.x0=0;
4738
 
4739
        В этом примере структура D наследует два экземпляра структуры A и
4740
    в ней находятся два элемента с именем x0. Компиляторы C++ при записи
4741
    типа d.x0=0 выдают сообщение об ошибке. C-- эту запись обрабатывает,
4742
    присваивание производится по умолчанию в элемент из последней базовой
4743
    структуры, имеющей элемент x0. Для того чтобы получить доступ ко
4744
    второму элементу x0 (физически этот элемент находится в структуре
4745
    первым), необходимо применить операцию разрешения видимости:
4746
 
4747
      d.B::x0=0;
4748
 
4749
        Из всего этого следует, что записи:
4750
 
4751
      d.x0=0;
4752
    и
4753
      d.C::x0=0;
4754
 
4755
         являются равнозначными.
4756
Return to contents.
4757
 
4758
 
4759
4760
    11.15.3 Наследование процедур.
4761
4762
 
4763
	Если в базовом классе есть процедура, а в производном классе Вы эту
4764
    процедуру переопределили, то эта процедура будет переопределена и в
4765
    базовом классе. Таким образом процедура определенная в базовом классе
4766
    будет потеряна. Пример:
4767
 
4768
    struct Point  // базовый класс
4769
    {
4770
    	int x; // элементы данных
4771
    	int y; // класса типа Point
4772
    	void SetX(int);  // объявление методов
4773
    	void SetY(int);  // класса Point
4774
    };
4775
 
4776
    void Point::SetX(int _x)  // определение процедуры класса Point
4777
    {
4778
    	IF((_x>=0)&&(_x<=MAX_X)) x=_x;
4779
    }
4780
 
4781
    struct Point2 : Point  // производный класс
4782
    {
4783
      int x2;
4784
    }
4785
 
4786
    struct Point3 : Point  // еще один производный класс
4787
    {
4788
      int z;
4789
    }
4790
 
4791
    void Point3::SetX(int _x)  // в этом производном классе переопределяем
4792
    {                          // процедуру SetX
4793
    	IF((_x>=80)&&(_x<=MAX_X)) x=_x;
4794
    }
4795
 
4796
	Процедура SetX, определенная в базовом классе Point, теперь будет
4797
    недоступна. Вместо кода определенного в этом классе, будет вызываться код
4798
    процедуры, определенный в наследуемом классе Point3. При вызове процедуры
4799
    SetX из другого производного класса Point2 будет также вызываться код
4800
    процедуры, определенный в производном классе Point3. Переопределяя
4801
    процедуру таким образом, Вы замените код этой процедуры в базовом классе и
4802
    во всех его наследуемых классах.
4803
 
4804
	Если Вам необходимо, чтобы код новой процедуры был доступен
4805
    одновременно с кодом старой процедуры, то в производном классе Вам
4806
    необходимо сделать еще одно объявление этой процедуры. Пример:
4807
 
4808
    struct Point  // базовый класс
4809
    {
4810
    	int x; // элементы данных
4811
    	int y; // класса типа Point
4812
    	void SetX(int);  // объявление методов
4813
    	void SetY(int);  // класса Point
4814
    };
4815
 
4816
    void Point::SetX(int _x)  // определение процедуры класса Point
4817
    {
4818
    	IF((_x>=0)&&(_x<=MAX_X)) x=_x;
4819
    }
4820
 
4821
    struct Point2 : Point  // производный класс
4822
    {
4823
      int x2;
4824
    }
4825
 
4826
    struct Point3 : Point  // еще один производный класс
4827
    {
4828
      int z;
4829
      void SetX(int);  // в наследуемом классе делаем еще одно объявление
4830
                       // процедуры SetX
4831
    }
4832
 
4833
    void Point3::SetX(int _x)  // в этом производном классе переопределяем
4834
    {                          // процедуру SetX
4835
    	IF((_x>=80)&&(_x<=MAX_X)) x=_x;
4836
	EDI=this;
4837
	EDI.Point.SetX(_x);  // делаем вызов одноименной процедуры из
4838
			     // базового класса
4839
    }
4840
 
4841
	Теперь из производного класса Point3 Вам доступны две различные
4842
    процедуры с одним именем SetX. А из базового класса Point и из другого
4843
    производного класса Point2 будет по прежнему доступен только базовый
4844
    вариант процедуры SetX.
4845
Return to contents.
4846
 
4847
 
4848
4849
12. Типы выходных файлов.
4850
 
4851
  12.1 Выходные файлы типа COM.
4852
4853
 
4854
      Этот тип выходного файла получается автоматически по умолчанию.
4855
 
4856
      Изначально C-- мог делать только файлы формата типа COM. В настоящее
4857
  время появилась возможность получать файла типа EXE с моделями памяти tiny
4858
  и small для 16-битного кода, а также 32-битные для DOS и Windows. Также
4859
  есть возможность получения выходного файла в формате OBJ, что позволяет
4860
  связывать программы на C-- с программами на других языках.
4861
Return to contents.
4862
 
4863
 
4864
4865
  12.2 Выходные файлы типа EXE.
4866
4867
 
4868
      Этот формат файла можно получить, если компилировать с ключом командной
4869
  строки /exe или /e.
4870
 
4871
      Возможно также поддержка EXE-формата через выходной файл формата OBJ,
4872
  который можно затем обработать линковщиком, не входящим в пакет C--.
4873
Return to contents.
4874
 
4875
 
4876
4877
  12.3 Выходной файл *.EXE с моделью памяти tiny.
4878
4879
 
4880
      Фактически код файла *.exe модели tiny ничем не отличается от кода
4881
  *.com. В сущности, это тот же com-файл, к которому добавлен 32-байтный
4882
  заголовок exe-файла. Единственное отличие возникает, когда Вы компилируете
4883
  файл с директивой ?resize TRUE. В com-файле, по этой директиве, в код
4884
  программы добавляется соответствующий код, изменяющий размер доступной
4885
  памяти. В exe-файле для этих целей будет скорректирован заголовок
4886
  exe-файла.
4887
 
4888
      Чтобы получить exe-файл с моделью памяти tiny, надо запустить
4889
  компилятор с ключом в командной строке /TEXE.
4890
Return to contents.
4891
 
4892
 
4893
4894
  12.4 Объектный выходной файл OBJ.
4895
4896
 
4897
      В настоящее время C-- может только создавать OBJ-файлы, но не может их
4898
  компоновать.
4899
 
4900
      Ранее C-- создавал obj-файлы, которые могли быть подключены к проектам
4901
  созданным на других языках, т.е. ведомые (slave) модули. Причем из C--
4902
  модулей для основного проекта были доступны только процедуры и эти
4903
  процедуры не должны были использовать глобальные переменные.
4904
 
4905
      Теперь же C-- может создавать основной модуль (master), который может
4906
  быть слинкован в самостоятельный файл.
4907
 
4908
      Для obj-файлов появилась возможность использовать внешние (extern)
4909
  процедуры, переменные или структуры. Для этого достаточно их объявить как
4910
  extern. Причем ключевое слово extern должно быть всегда первым. Пример
4911
  объявления внешних объектов:
4912
 
4913
  extern void cdecl _printf(); // объявление внешней процедуры _printf имеющей
4914
                               // тип cdecl  и тип возврата void
4915
  extern int buts,cubs;        // объявление двух внешних переменных типа int
4916
  extern struct IPXL ipxl;     // объявление внешней структуры ipxl имеющей тег
4917
  			     // IPXL,  причем тег этой структуры должен быть
4918
  			     // описан ранее.
4919
 
4920
      Появление возможности объявлять внешние объекты позволяет подключать к
4921
  obj-модулю на C-- модули написанные на других языках или подключать к
4922
  программе на C-- процедуры из библиотек на других языках. При объявлении
4923
  внешних объектов очень важно правильно указать тип процедуры и ее имя. Если
4924
  Вы будете использовать внешние процедуры, написанные на C то чаще всего,
4925
  Вам нужно будет указывать модификатор cdecl, а к имени процедуры или
4926
  переменной добавлять префикс _.
4927
 
4928
      Из основного (master) obj-файла написанного на C-- для других
4929
  obj-модулей доступны все процедуры, глобальные переменные и глобальные
4930
  структуры.
4931
 
4932
      Чтобы получить ведомый obj-модуль при компиляции надо использовать ключ
4933
  /sobj.
4934
 
4935
      C-- может создавать obj-файлы с моделью памяти tiny и small. По
4936
  умолчанию создаются модули с моделью tiny. Чтобы получить obj-файл с
4937
  моделью памяти small надо запустить компилятор с ключами /obj и /exe.
4938
 
4939
      Для создания obj-файлов для 32-битного DOS в командной строке Вам
4940
  необходимо указать ключи /d32 и /obj. Использовать полученный obj-файл мне
4941
  удалось лишь с помощью wlink и расширителя zrdx.exe.
4942
 
4943
      Создание obj-файлов под windows не предусмотрено.
4944
Return to contents.
4945
 
4946
 
4947
4948
  12.5 COM файл symbiosis.
4949
 
4950
    12.5.1 СИМБИОЗ - что это такое?
4951
4952
 
4953
        Транслятор C-- имеет ключ, позволяющий добавлять компилируемую
4954
    программу к концу уже имеющегося COM файла. Это называют COM-файл
4955
    Symbiosis. Когда такая программа запускается, управление сначала получает
4956
    добавленный код C--, и только после выполнения его процедуры main()
4957
    управление получит первоначальный код COM-файла.
4958
 
4959
        Если добавленный вами код завершается EXIT() или ABORT(), программа
4960
    прекратится, и первоначальный код COM-файла не будет выполнен. Это
4961
    позволяет программе, добавленной к COM файлу, определять, будет ли
4962
    управление передано на первоначальный код.
4963
Return to contents.
4964
 
4965
 
4966
4967
    12.5.2 Как это делать.
4968
4969
 
4970
        Чтобы сделать это, Вы должны использовать ключ /SYM в командной
4971
    строке компилятора, в которой указывается полное имя COM-файла, к
4972
    которому что-то добавляется. При этом оригинал COM-файла не меняется, а
4973
    новый файл содержит его в себе. Например, чтобы откомпилировать программу
4974
    HELLO.C-- к концу копии C:\command.сом используют следующую команду:
4975
 
4976
             C-- /SYM C:\COMMAND.COM HELLO.C--
4977
 
4978
    Будет создан выходной файл HELLO.COM .
4979
Return to contents.
4980
 
4981
 
4982
4983
    12.5.3 Использование.
4984
4985
 
4986
        Вы можете, вероятно, придумать большое количество путей использования
4987
    этой функции, типа:
4988
 
4989
             - Добавление защиты с использованием пароля к некоторым
4990
	       специальным COM файлам.
4991
             - Уменьшение памяти, доступной COM файлу при запуске.
4992
             - Инициализация режима видео для COM файла.
4993
Return to contents.
4994
 
4995
 
4996
4997
    12.5.4 Злоупотребления.
4998
4999
 
5000
        Любой злоумышленник может придумать и вредные применения для этой
5001
    функции. Наиболее очевидное из них - создание троянских коней. Я хотел бы
5002
    указать, что это неконструктивное использование C--, и любое
5003
    разрушительное использование симбиозов COM-файлов запрещено.
5004
Return to contents.
5005
 
5006
 
5007
5008
  12.6 SYS - драйверы устройств.
5009
5010
 
5011
      Компилятор значительно облегчит Ваш труд при написании драйверов.
5012
  Компилятор сам создаст заголовок драйвера и процедуры СТРАТЕГИЯ и
5013
  ПРЕРЫВАНИЕ. Вам остается лишь написать код обработки команд.
5014
 
5015
      Что бы откомпилировать файл драйвера устройства, надо добавить в
5016
  командную строку ключ /SYS. Кроме того, появились новые директивы
5017
  компилятору, которые действуют только с этим ключом. Вот они:
5018
 
5019
      ?sysattribute значение  - эта  директива передает  компилятору
5020
  атрибут создаваемого драйвера. По умолчанию устанавливается значение
5021
  0x2000.
5022
 
5023
      ?sysname <текстовая  строка> -  эта директива  передает компилятору
5024
  имя будущего драйвера. По умолчанию присваивается имя "NO_NAME". Длина
5025
  имени  не более 8 символов.
5026
 
5027
      ?syscommand command_0,command_1, ... command_n; - эта директива
5028
  является обязательной. По этой директиве компилятору передается список имен
5029
  процедур обработки команд драйвера. Имена разделены запятыми. Список должен
5030
  заканчиваться символом точка-с-запятой. Можно передать не более 25 команд.
5031
  Если какая-то команда не имеет кода поддержки, то в список надо записать
5032
  слово NONE.
5033
 
5034
      По умолчанию компилятор для драйвера не создает стек. Драйвер может
5035
  пользоваться системным стеком. Но, говорят, что он имеет маленькую глубину.
5036
  Если Ваши процедуры активно используют стек, и Вы не надеетесь на системный,
5037
  то директивой ?stack <величина> можно заставить драйвер пользоваться своим
5038
  стеком.
5039
 
5040
      Вашим процедурам обработки команд при передаче управления в регистрах
5041
  ES:BX будет передан адрес заголовка запроса. Регистр DS равен CS. При
5042
  возврате управления ваши процедуры должны сохранить регистр DS. В регистре
5043
  AX должен находиться код возврата. Остальные регистры могут быть
5044
  использованы произвольным образом.
5045
 
5046
      Процедуру обработки команды инициализации желательно располагать
5047
  последней (чтобы иметь возможность отдать адресное пространство занимаемое
5048
  этой процедурой операционной системе). Перед этой процедурой, если Вы в
5049
  других процедурах обработки команд используете динамические процедуры,
5050
  обязательно должна быть директива ?setdinproc. Глобальные переменные должны
5051
  быть обязательно проинициализированы.
5052
Return to contents.
5053
 
5054
 
5055
5056
  12.7 Компиляция кода расширителей ROM-BIOS.
5057
5058
 
5059
      Расширители ROM-BIOS (BIOS видеоконтроллеров, сетевых карт...) имеют
5060
  определенную структуру и требования. C-- теперь может облегчить Вам процесс
5061
  создания кода ROM-BIOS. Если запустить компилятор на компиляцию с ключом
5062
  командной строки /ROM, то компилятор создаст сигнатуру (заголовок)
5063
  ROM-BIOS, заполнит оставшееся свободное место до указанного размера ПЗУ
5064
  кодом заполнения, подсчитает и скорректирует контрольную сумму ПЗУ.
5065
 
5066
      Для этого режима компиляции есть несколько специфических директив:
5067
 
5068
    1.  ?sizerom value - эта директива сообщает компилятору размер ПЗУ в
5069
  байтах. Если эта директива не указана, то компилятор сам выберет
5070
  минимальный подходящий размер ПЗУ из ряда: 1024, 2048, 4096, 8192, 16384,
5071
  32762 или 65536. Свободное от кода и данных место будут заполнены до конца
5072
  размера ПЗУ байтом заполнения определяемого директивой ?aligner. По
5073
  умолчанию он равен нулю, для РПЗУ типа 27ххх этот байт имеет смысл сделать
5074
  равным 0xFF. Последний байт ПЗУ будет скорректирован компилятором таким
5075
  образом, чтобы контрольная сумма равнялась нулю.
5076
 
5077
    2.  ?movedatarom TRUE/FALSE - эта директива сообщает компилятору есть ли
5078
  необходимость копировать данные из ПЗУ в ОЗУ. По умолчанию она установлена
5079
  в FALSE. Если эту директиву определить TRUE, то компилятор вставит в
5080
  область инициализации код перемещающий данные из ПЗУ в ОЗУ. При этом
5081
  регистр DS будет установлен на сегмент ОЗУ. Стек также будет переустановлен
5082
  на этот сегмент. Таким образом, процедура main получит управление с
5083
  регистрами AX = ES = DS = SS = сегменту ОЗУ с перенесенными в него данными.
5084
  Если эту директиву установить в FALSE, регистр DS все равно будет
5085
  переустановлен на адрес сегмента ОЗУ, так как Ваш код будет использовать
5086
  этот сегмент для неинициализированных глобальных переменных.
5087
  Инициализированные переменные останутся в ПЗУ и все обращения к ним будут
5088
  производиться через регистр CS. Так же останется не тронутым (таким, каким
5089
  его установил главный BIOS) и стек.
5090
 
5091
    3.  ?dataseg value - этой директивой компилятору сообщается сегментный
5092
  адрес ОЗУ, который может быть использован вашим кодом. По умолчанию он
5093
  равен 0x70. Этот адрес вы можете узнать в любой момент, считав его из вашего
5094
  кода по смещению 4. Например: DS = CSWORD[4];
5095
 
5096
      Некоторые замечания:
5097
 
5098
    1.  Не забывайте, что в момент инициализации ROM-BIOS, DOS еще не
5099
  загружен, и соответственно все процедуры использующие вызовы DOS работать
5100
  не будут.
5101
 
5102
    2. Нельзя завершать работу программы процедурами ABORT() или EXIT() и им
5103
  подобным. Работа расширителя ROM-BIOS должна завершаться только выходом из
5104
  процедуры main().
5105
 
5106
    3. Если директива ?movedatarom установлена в FALSE, то будьте внимательны
5107
  при работе с инициализированными переменными. Они в этом режиме доступны
5108
  только для чтения, и адресуются через регистр CS.
5109
Return to contents.
5110
 
5111
 
5112
5113
  12.8 32-битные файлы.
5114
 
5115
    12.8.1 32-битный код под DOS.
5116
5117
 
5118
        Для того чтобы откомпилировать 32-битную программу под DOS надо
5119
    запустить компилятор с ключом командной строки /d32. Но работа 32-битной
5120
    программы под DOS-ом невозможна без расширителя DOS. Для C-- можно
5121
    использовать DOS4GW или zrdx.exe или любой другой расширитель DOS. Чтобы
5122
    компилятор знал, где искать stub файл и его имя, надо в файл c--.ini
5123
    прописать строку stub=path_name_to_stub_file.  Пример:
5124
 
5125
        stub=c:\c--\zrdx.exe
5126
 
5127
        Если не добавлять в c--.ini эту строку, то компилятор сгенерирует
5128
    32-битный exe-файл, но без расширителя DOS. Если в командной строке
5129
    вместе с ключом /d32 указать и ключ /ns, то строка с переменной stub из
5130
    файла c--.ini будет аннулирована, и вы получите файл без расширителя DOS.
5131
 
5132
        Для 32-битного DOS-файла можно использовать директивы компилятора
5133
    ?parsecommandline TRUE/FALSE или его расширенный вариант ?argc
5134
    TRUE/FALSE.  Реализована и поддержка директивы ?atexit TRUE/FALSE.
5135
 
5136
        Сейчас для 32-битных DOS-файлов используется LE-формат. Так как LE
5137
    формат является стандартным, то теперь можно использовать почти любой
5138
    stub, понимающий этот формат. Файлы LE формата можно сжимать программами
5139
    типа UPX.EXE и ей подобными.
5140
 
5141
        Если Вы используете stub, который затем загружает DOS4GW.EXE, то
5142
    начало Вашей программы должно иметь специальную сигнатуру. Компилятор
5143
    автоматически сформирует ее, если Вы в командной строке или в c--.ini
5144
    файле укажете ключ /DOS4GW. Такой ключ Вам необходимо будет применять,
5145
    если Вы будете использовать в качестве stub 4gs.exe.
5146
 
5147
        Существует также поддержка блока кода использующего для перехода и
5148
    работы в 32-битном режиме возможности DPMI сервиса. Исходный текст этого
5149
    блока находится в файле startup.h-- и компилируется, если в командной
5150
    строке указана опция /stub=dpmi или в файле c--.ini написать строку
5151
    stub=dpmi.  Недостатком этого способа перехода и работы в 32-битном
5152
    режиме являются необходимость обязательного функционирования на
5153
    запускаемом компьютере DPMI сервиса. Так как, программа загружается как
5154
    обычная DOS программа, и лишь в процессе работы переходит в 32-битный
5155
    режим работы, размер программы ограничен размером свободной DOS памяти.
5156
    Ну а преимуществом его является компактный размер исполняемого файла.
5157
Return to contents.
5158
 
5159
 
5160
5161
    12.8.2 32-битный код под Windows.
5162
5163
 
5164
        Для того чтобы откомпилировать программу, написанную под Windows надо
5165
    запустить компилятор с ключом командной строки /w32.
5166
 
5167
        Если Вы в своей программе используете вызовы API-процедур, то эти
5168
    процедуры надо предварительно обязательно объявить. Объявление процедур
5169
    имеет следующую форму:
5170
 
5171
    extern WINAPI "DLL_name"
5172
    {
5173
        returncode procname1();
5174
        returncode procname2();
5175
        procname3();
5176
    }
5177
 
5178
      где:
5179
           DLL_name - имя и расширение dll-библиотеки, в которой находятся эти
5180
                      процедуры.
5181
         returncode - тип возврата из api-процедур. По умолчанию он равен dword.
5182
 
5183
        Программы, написанные под Windows, имеют одну немаловажную
5184
    особенность - все параметры в стековые процедуры передаются в обратном
5185
    порядке (так называемый C-стиль), но очистка стека от параметров
5186
    происходит в самих процедурах. Получается своеобразный гибрид C и pascal
5187
    стилей - stdcall.
5188
 
5189
        С помощю ключа /W32C компилятор создает консольный файл под Windows.
5190
 
5191
	Если при компиляции указывали опцию командной строки /j0 или
5192
    директиву #jumptomain NONE, то Ваша программа будет компилироваться без
5193
    использования кода начальной инициализации, описание которого находится в
5194
    файле startup.h--.
5195
 
5196
	Код начальной инициализации для программ под Windows имеет следующий
5197
    вид:
5198
 
5199
	hThisInst=GetModuleHandleA(0);
5200
      #ifdef __CONSOLE__
5201
	hStdOut=GetStdHandle(-11);
5202
      #endif
5203
        lpszArgs=GetCommandLineA();
5204
      #ifdef __environ;
5205
	environ=GetEnvironmentStringsA();
5206
      #endif
5207
	main();
5208
	ExitProcess(EAX);
5209
 
5210
	Таким образом, в глобальных переменных hThisInst будет находится
5211
    handl запущенного файла, а в lpszArgs адрес командной строки Вашего
5212
    файла. Если Вы в командной строке указали опции /p или /argc или в
5213
    начале вашего файла есть директивы #parsecommandline TRUE или argc TRUE,
5214
	то компилятор создаст дополнительный код сделающий разборку этой
5215
    командной строки на части. Если Вы компилируете консольную программу, то
5216
    в вашей программе будет еще одна глобальная переменная - hStdOut. В этой
5217
    переменной хранится handl стандартного вывода (экрана). Если Вы при
5218
    компиляции программы указали опцию /env, то в глобальной переменной
5219
    environ хранится адрес переменной окружения программы.
5220
 
5221
	После завершения работы процедуры main выполнятся процедура
5222
    ExitProcess, которой в качестве параметра передается регистр EAX. Т.о.
5223
    Вам для завершения работы программы будет достаточно сделать выход из
5224
    процедуры main, предварительно загрузив в регистр EAX нужный Вам код
5225
    возврата.
5226
 
5227
        Некоторые компиляторы создают DLL, в которых имена экспортируемых
5228
    процедур имеют такой формат:
5229
 
5230
       ProcName@8
5231
 
5232
        В этом имени после символа @ указывается размер стека с
5233
    параметрами, передаваемых процедуре.
5234
 
5235
        Объявлять такие процедуры нужно так:
5236
 
5237
    extern WINAPI "name.dll"
5238
    {
5239
       ProcName@8 ;
5240
    }
5241
 
5242
    т.е. без круглых скобок. В программе, при обращении к такой процедуре, ее
5243
    имя надо писать без суффикса @8, т.е. вот так - ProcName(param1,param2);
5244
Return to contents.
5245
 
5246
 
5247
5248
    12.8.3 Вызов API процедур по ординалам.
5249
5250
 
5251
        В динамически подключаемых библиотеках (DLL) каждой процедуре, кроме
5252
    ее имени, соответствует уникальное число, которое называется ординалом. И
5253
    поэтому, кроме общепринятого вызова API-процедуры по имени, можно делать
5254
    вызов и по ординалу. Теоретически, при использовании вызова по ординалу,
5255
    загрузка файла должна происходить быстрее. Так как в выходной файл не
5256
    будут включены списки имен процедур, вызов которых производится по
5257
    ординалам, то выходной файл может получиться немного меньшим по размеру.
5258
 
5259
        Чтобы компилятор создал файл, использующий вызов API-процедур по
5260
    ординалам, надо сделать две вещи:
5261
 
5262
     1. Разрешить компилятору это делать. Для этого надо в опциях командной
5263
    строки (или в файле C--.INI) указать ключ WO.
5264
 
5265
     2. Сообщить компилятору - какой номер ординала соответствует какому
5266
    имени процедуры. Процедуры, для которых не был указан ординал, будет
5267
    создан вызов по имени. Установить соответствие имен процедур ординалу
5268
    можно двумя способами:
5269
 
5270
        a). Автоматически, с помощью опции командной строки IND=name.dll,
5271
	по которой компилятор просканирует эту библиотеку и импортирует из
5272
	нее все имена и ординалы процедур. (Импорт возможет только из
5273
	библиотек имеющих формат PE).
5274
 
5275
        b). В ручную указать в объявлении API-процедур и ее ординал. Делается
5276
	это так: после имени процедуры ставится точка, а за ней указывается
5277
	номер ординала. Вот пример объявления API-процедуры с указанием ее
5278
	ординала:
5279
 
5280
    extern WINAPI "user32.dll"
5281
    {
5282
      ............
5283
      long  MessageBoxA.429();
5284
      ............
5285
    }
5286
 
5287
        В библиотеках (DLL), иногда существуют процедуры, для которых не
5288
    указано их имя, но указан номер ординала. Вызов таких процедур по имени
5289
    не возможен, но можно это сделать по ординалу (если, конечно Вы знаете,
5290
    для чего эта процедура и что она делает). Для этого в объявлении
5291
    API-процедуры Вам надо придумать для этой процедуры уникальное имя и
5292
    указать реальный ординал. Затем в программе Вы будете обращаться к этой
5293
    процедуре по вымышленному имени. Но если Вы случайно откомпилируете такой
5294
    файл без ключа WO, то при запуске этой программы Вы получите сообщение,
5295
    о том, что данного имени в библиотеке нет.
5296
 
5297
        К сожалению, нет никаких гарантий того, что номер ординала для данной
5298
    процедуры не изменится при смене версии динамической библиотеки. Поэтому
5299
    использовать ординалы надо осторожно.
5300
Return to contents.
5301
 
5302
 
5303
5304
    12.8.4 Создание DLL под Windows.
5305
5306
 
5307
        Динамически подключаемые библиотеки позволят получать более
5308
    компактные программы и ускорить процесс компиляции. К минусам
5309
    использования DLL можно отнести необходимость наличия самих файлов DLL на
5310
    запускаемом компьютере и немного увеличивается время запуска программы.
5311
 
5312
        Для того чтобы процедура стала доступной для других программ надо в
5313
    исходном тексте перед именем процедуры прописать ключевое слово - _export.
5314
    Пример:
5315
 
5316
      void _export testproc()
5317
      {
5318
        ....
5319
      }
5320
 
5321
        Для того чтобы создать DLL, нужно написать файл, в котором будут
5322
    процедуры с ключевыми словами _export. Вспомогательные процедуры, которые
5323
    могут понадобиться для работы основных экспортируемых процедур, объявлять
5324
    как _export необязательно. Затем этот файл нужно откомпилировать с ключом
5325
    /dll.  В результате Вы получите готовую динамически подключаемую
5326
    библиотеку.
5327
Return to contents.
5328
 
5329
 
5330
5331
    12.8.5 Инициализация DLL при загрузке.
5332
5333
 
5334
        Иногда, для работы процедур из динамических библиотек (DLL), бывает
5335
    необходимым инициализировать некоторые переменные значениями, зависящими
5336
    от текущего состояния операционной системы, например, получить дескриптор
5337
    этой библиотеки.
5338
 
5339
      Директивой #jumptomain NONE (-j0) управление при запуске передается
5340
    сразу на процедуру main.
5341
 
5342
      Во всех остальных случаях генерируется код заглушки и управление на
5343
    процедуру main не передается. Фактически процедура main в этом случае не
5344
    нужна.
5345
 
5346
      Процедура main при создании файлов DLL должна выглядеть немного иначе,
5347
    чем в других случаях:
5348
 
5349
    dword main ( dword hInstDLL, reason, reserv )
5350
    {
5351
      ...
5352
    }
5353
Return to contents.
5354
 
5355
 
5356
5357
    12.8.6 Компиляция ресурсов.
5358
5359
 
5360
        Встроенный в C-- компилятор ресурсов по своим возможностям уступает
5361
    специализированным компиляторам ресурсов, но этих возможностей, как мне
5362
    кажется, будет достаточно для большинства Ваших задач.
5363
 
5364
        Будет проще перечислить то, что встроенный в C-- компилятор ресурсов
5365
    не умеет делать. Не обрабатываются операторы ресурсов: VERSION,
5366
    VERSIONINFO и определяемые пользователем ресурсы. При необходимости,
5367
    данные, вводимые с помощью этих операторов, можно ввести с помощью
5368
    оператора RCDATA. У многих операторов ресурсов есть необязательные
5369
    параметры loading и 'memory'.  Поддержка этих параметров не
5370
    реализована. Встретив эти параметры, компилятор их просто пропустит.
5371
 
5372
        Заставить компилятор C-- обрабатывать ресурсы можно двумя способами:
5373
 
5374
        1. Включить в свой проект директивой #include файл с расширением
5375
    .rc.  Файлы с таким расширением компилятор считает файлом с ресурсами.
5376
    Файл ресурсов необходимо включать в Ваш проект лишь после включения
5377
    заголовочных файлов Windows.
5378
 
5379
        2. Ресурсы можно располагать в теле исходного текста программы в
5380
    произвольном месте. Текст ресурсов должен начинаться с директивы #pragma
5381
    resource start, а заканчиваться директивой #pragma resoutce end.
5382
    Ресурсы могут быть разделенными на части и эти части можно располагать в
5383
    любом удобном для Вас месте (глупо располагать ресурсы в блоке
5384
    комментариев и потом удивляться, почему они не были откомпилированы).
5385
    Компилятор соберет эти части и откомпилирует.
5386
 
5387
        Имена операторов можно писать как большими, так и маленькими буквами,
5388
    но имена идентификаторов чувствительны к регистру.  В тексте ресурсов
5389
    можно использовать директивы и комментарии.
5390
 
5391
        Ничто не мешает Вам использовать компиляторы ресурсов от других
5392
    языков.  Главное, чтобы синтаксис файла ресурсов соответствовал выбранному
5393
    компилятору.
5394
Return to contents.
5395
 
5396
 
5397
5398
  12.9 Выходные файлы для MeOS.
5399
5400
 
5401
      Исполняемые файлы для операционной системы MenuetOS поддерживаются
5402
  компилятором совсем недавно. Для того, чтобы откомпилировать файл для
5403
  MenuetOS, нужно в опциях компилятору указать /meos. Вы получите файл без
5404
  расширения, который потом можно будет выполнить в среде операционной
5405
  системы MenuetOS.
5406
 
5407
      Если при компиляции файла Вы не указывали опцию /j0 или не
5408
  использовали директиву #jumptomain NONE, то компилятор будет использовать
5409
  файл начальной инициализации startup.h--, в котором для операционной
5410
  системы MenuetOS создан блок инициализации и завершения программы.
5411
  Завершать выполнение таких программ можно просто выйдя из процедуры main.
5412
Return to contents.
5413
 
5414
 
5415
5416
13. Приложения.
5417
 
5418
  13.1 Поиск включаемых файлов.
5419
5420
 
5421
      Поиск включаемого в вашу программу файла, имя которого объявляется
5422
  директивой include и заключено в двойные кавычки "", производится
5423
  компилятором по такой схеме:
5424
 
5425
  сначала делается попытка открыть файл в текущей директории. Если файла там
5426
  нет, то далее делается попытка открыть файл в директории указанной
5427
  директивой #includepath. Если директива не была задана или файла в этой
5428
  директории не оказалось, то делается попытка открыть файл в директории
5429
  указанной в командной строке командой /ip=path. Если эта команда не была
5430
  задана или файла в указанной директории не оказалось, то делается попытка
5431
  открыть файл в директории указанной в файле C--.INI командой ip=. Если эта
5432
  команда не была задана или файла в указанной директории не оказалось, то
5433
  делается попытка открыть файл в директории, на которую указывает переменная
5434
  окружения C--. Если переменная окружения не была задана или файла в этой
5435
  директории не оказалось, то делается последняя попытка открыть файл в
5436
  директории, откуда был запущен компилятор.
5437
 
5438
      Если имя включаемого файла заключено в угловые скобки < >, то поиск
5439
  этого файла производится в противоположном направлении, за исключением
5440
  того, что поиск в текущей директории не производится.
5441
 
5442
      Для консольной версии компилятора имена главного модуля и включаемых
5443
  файлов могут иметь длину более 8 символов.
5444
Return to contents.
5445
 
5446
 
5447
5448
  13.2 Регистры, которые должны быть сохранены.
5449
5450
 
5451
      Регистры, которые должны сохраняться - BP, DI, SI, DS, SS, SP, CS и IP.
5452
 
5453
      BP используется как указатель на локальные и параметрические
5454
  переменные в стеке, что и требует его сохранения.
5455
 
5456
      DI и SI сохранять не обязательно, если программист осознает
5457
  последствия. DI и SI часто используются для индексации массивов, как
5458
  например в формуле:
5459
 
5460
        dog = firehydrant(1,red) + legs[DI];
5461
 
5462
      Если DI не сохранялся в процедуре firehydrant, значение, присвоенное
5463
  переменной dog, скорее всего, будет неправильным, поскольку индекс для
5464
  массива legs был изменен. В сущности, для точного согласования все
5465
  процедуры должны иметь специальное указание в комментарии на то, что в них
5466
  не сохраняется содержимое регистров DI и/или SI.
5467
 
5468
      DS указывает на сегмент данных, и все операции с глобальными
5469
  переменными пользуются этим значением.
5470
 
5471
      SS хранит сегмент стека и должен сохраняться. SP указывает на текущую
5472
  позицию в стеке и тоже должен сохраняться.
5473
 
5474
      CS хранит сегмент кода программы.  Все команды выбираются с
5475
  использованием CS и IP, следовательно их значения должны сохраняться. IP,
5476
  как известно, указатель адреса команды, и CS и IP непосредственно не могут
5477
  изменяться в процессорах 8086, 8088, 80286, 80386, 80486,...
5478
Return to contents.
5479
 
5480
 
5481
5482
  13.3 C--.ini файл.
5483
5484
 
5485
      C--.ini файл предназначен для предустановки по умолчанию параметров
5486
  компилятора.
5487
 
5488
      Сейчас компилятор поддерживает огромное число параметров командной
5489
  строки. Правильное их использование позволит Вам получать более компактный
5490
  код и может значительно облегчить Вам отладку программы. Но так как этих
5491
  параметров очень много набирать их каждый раз в командной строке бывает
5492
  утомительно и не исключена возможность пропустить какой-нибудь параметр.
5493
  Чтобы избавить Вас от всех этих напастей и был введен c--.ini файл.
5494
 
5495
       Параметры командной строки прописываются в этом файле построчно.
5496
  Синтаксис тот же, что и в командной строке, но без ведущего обратного слэша
5497
  или минуса. Если файл расположен в директории, на которую указывает
5498
  переменная окружения set c--= или если эта переменная не определена,
5499
  то в той же директории где и файл c--.exe, то эти параметры
5500
  распространяются на все компилируемые программы. Если же файл c--.ini
5501
  расположен в текущей директории, то параметры считываются только из этого
5502
  файла и действуют только для текущего проекта.
5503
 
5504
      Допустимо использование комментариев. Признаком начала комментария
5505
  является символ ;. Все последующие символы после ; и до конца строки
5506
  считаются комментарием.
5507
 
5508
      Пример C--.ini файла:
5509
 
5510
  r-
5511
  X
5512
  3     ;это комментарий
5513
  os
5514
 
5515
      ini-файл может иметь любое имя (но расширение должно быть обязательно
5516
  ini). Имя этого файла с расширением должно быть передано компилятору в
5517
  командной строке. Файл c--.ini загружается и обрабатывается автоматически
5518
  до загрузки файла указанного в командной строке.
5519
 
5520
      Таким образом, файл *.ini можно использовать подобно make-файлу - в нем
5521
  Вы можете указать и имя главного компилируемого модуля, и все необходимые
5522
  для его компиляции настройки.
5523
 
5524
      Как альтернативу c--.ini файлу, параметры командной строки можно
5525
  прописывать непосредственно в начале главного файла компилируемого проекта,
5526
  используя директиву pragma option. С одной стороны это обеспечит Вашему
5527
  проекту независимость от настроек компилятора, если Ваш проект будет
5528
  компилироваться на другом компьютере. Но с другой стороны некоторые
5529
  настройки являются индивидуальными для данного компьютера (это расположение
5530
  библиотек, имена и расположение stub-файлов). Какой вариант использовать
5531
  решать Вам, но как говорят, и я с этим согласен, лучше пользоваться золотой
5532
  серединой - Часть параметров прописать в c--.ini файле, а другую
5533
  непосредственно в компилируемом файле.
5534
Return to contents.
5535
 
5536
 
5537
5538
  13.4 startup.h-- файл.
5539
5540
 
5541
      В этом файле находятся исходные тексты, которые компилируются
5542
  компилятором в код начальной инициализации файла, для всех поддерживаемых
5543
  компилятором типов выходных файлов. Этот файл должен находится либо в
5544
  директории вместе с компилятором, либо в директории с библиотечными файлами.
5545
  Этот файл включается компилятором в проект автоматически, а включение его
5546
  директивой include может привести к нежелательным результатам.
5547
 
5548
      В блоке начальной инициализации программы может производится (если Вы
5549
  это укажете с помощью опций командной строки или используя директивы),
5550
  разбор командной строки на параметры, сохранение переменой окружения,
5551
  поддержка работы процедуры ATEXIT, изменение размера доступной памяти для
5552
  *.com файлов и многие другие подготовительные операции. Если Вы
5553
  откомпилируете свой файл не используя никаких опций командной строки и у
5554
  Вас будет отсутствовать c--.ini файл, а в самом компилируемом файле у Вас
5555
  будут отсутствовать директивы, то при компиляции *.com файла в него будет
5556
  включен блок изменяющий размер доступной памяти и сигнатура SPHINXC--.
5557
 
5558
      Если Вы компилируете файл типа *.exe (кроме файла модели tiny для DOS)
5559
  и используете директиву jumptomain NONE или ключ командной строки /j0,
5560
  то для этого проекта файл startup.h-- компилятором не используется. Не
5561
  используется этот файл также при компиляции *.com файлов если, кроме /j0,
5562
  в этом проекте не используется разбор командной строки (/p /argc), не
5563
  применяется процедура ATEXIT (/at), не используется адрес переменной
5564
  окружения (/env), не используется очистка области post-адресов (/cpa), не
5565
  используется уменьшение доступной программе памяти (/r) и не используется
5566
  заглушка нажатий CTRL-C (/c).
5567
 
5568
      Кроме блока начальной инициализации программы в файле startup.h--
5569
  находятся динамические процедуры:
5570
 
5571
  void CLEARPOSTAREA( (E)AX );  - очистка post-области данных.
5572
  unsigned int PARAMSTR( ECX ); - получить адрес элемента командной строки
5573
  unsigned int PARAMCOUNT();    - получить число элементов в командной строке
5574
 
5575
      При разборе командной строки на составляющие ее элементы для 32-битных
5576
  программ реализована поддержка длинных имен. Для 16-битных программ
5577
  поддержка разбора командной строки с учетом длинных имен подключается, если
5578
  Вы в начале свой программы укажете директиву:
5579
 
5580
  #define _USELONGNAME TRUE
5581
 
5582
      либо в c--.ini файле или в командной строке компилятора укажете опцию
5583
  d=_USELONGNAME.
5584
Return to contents.
5585
 
5586
 
5587
5588
  13.5 mainlib.ldp файл.
5589
5590
 
5591
      В этом файле находится большое число процедур из основной библиотеки
5592
  компилятора в уже откомпилированном виде. Все процедуры откомпилированы в
5593
  4-х различных режимах оптимизации. В этот файл также вынесены многие
5594
  процедуры, которые ранее были внутри компилятора. Использование ранее
5595
  откомпилированных процедур повышает скорость компиляции.
5596
 
5597
      Эти процедуры откомпилированы только для 16-битного режима работы
5598
  программы. Если Вы будете использовать эти процедуры в 32-битной программе,
5599
  то компилятор на это не выдаст никаких сообщений и включит эту процедуру в
5600
  Ваш код. Но при запуске такой программы она неизбежно потерпит крах.
5601
 
5602
      Использовать эту библиотеку очень просто. Все что нужно, это
5603
  расположить эту библиотеку в одной с компилятором директории. Тогда
5604
  компилятор, если встретит в вашей программе вызов процедуры, которая не
5605
  была определена ни во включаемых в программу библиотечных файлах, ни в
5606
  вашей программе, будет искать эту процедуру в файле mainlib.ldp. Если эта
5607
  процедура будет найдена в этом файле, то ее код будет перенесен в Ваш файл,
5608
  иначе будет выдано сообщение о неизвестной процедуре. Таким образом, чтобы
5609
  процедура была вставлена в вашу программу из библиотеки mainlib.ldp Вам
5610
  нужно в свою программу не включать библиотечный файл, содержащий процедуру с
5611
  таким же именем.
5612
 
5613
      Список процедур находящихся в этой библиотеке можно получить с помощью
5614
  специальной программы cmmlib.exe. Эту программу можно найти в архиве
5615
  cmmlib.rar. Извлеките программу cmmlib.exe из этого архива и расположите ее
5616
  в одной с компилятором директории. Затем запустите эту программу с ключом
5617
  /L и Вы получите список процедур находящихся в этой библиотеке.
5618
Return to contents.
5619
 
5620
 
5621
5622
  13.6 C-- символы.
5623
5624
 
5625
  SYMBOL|FUNCTION                  |EXAMPLE
5626
  --------------------------------------------------------------------
5627
    /*  |начинают блок комментария |/* комментарий */
5628
    */  |завершают блок комментария|/* комментарий */
5629
        |                          |
5630
    //  |комментарий до конца линии|// комментарий
5631
        |                          |
5632
     =  |присвоение                |AX = 12;
5633
     +  |сложение                  |AX = BX + 12;
5634
     -  |вычитание                 |house = dog - church;
5635
     *  |умножение или указатель   |x = y * z; AL = * var;
5636
     /  |деление                   |x1 = dog / legs;
5637
     &  |поразрядное логическое И  |polution = stupid & pointless;
5638
     |  |поразрядное логическое ИЛИ|yes = i | mabe;
5639
     ^  |поразрядн. исключающее ИЛИ|snap = got ^ power;
5640
    <<  |битовый сдвиг влево       |x = y << z;
5641
    >>  |битовый сдвиг вправо      |x = y >> z;
5642
        |                          |
5643
    +=  |сложение                  |fox += 12;   // fox = fox +12;
5644
    -=  |вычитание                 |cow -= BX;   // cow = cow - BX;
5645
    *=  |умножение                 |a *= b;      // a = a * b;
5646
    /=  |деление                   |a /= b;      // a = a / b;
5647
    &=  |поразрядное логическое И  |p &= q;      // p = p & q;
5648
    |=  |поразрядное логическое ИЛИ|p |= z;      // p = p | z;
5649
    ^=  |поразрядн. исключающее ИЛИ|u ^= s;      // u = u ^ s;
5650
    <<= |битовый сдвиг влево       |x <<= z;     // x = x << z
5651
    >>= |битовый сдвиг вправо      |x >>= z;     // x = x >> z
5652
        |                          |
5653
    ><  |обмен значениями          |x >< y; /* меняет местами значения x и y */
5654
        |                          |
5655
    ==  |проверка на равенство     |IF(AX == 12)
5656
     >  |проверка на больше чем    |IF(junk > BOGUS)
5657
     <  |проверка на меньше чем    |if( x < y )
5658
    >=  |проверка больше или равно |if(AX >= 12)
5659
    <=  |проверка меньше или равно |IF(BL >= CH)
5660
   !=   |проверка на неравенство   |IF(girl != boy)
5661
    <>  |проверка на отличие       |if (cat<>dog) /* та же функция что != */
5662
        |                          |
5663
    @   |вставка кода              |@ COLDBOOT(); /* вставляет COLDBOOT код */
5664
    :   |динамическая процедура    |: functionname () //объявляет functionname
5665
    $   |ассемблерная команда      |$ PUSH AX   /* заносит AX в стек */
5666
    #   |получение адреса(смещения)|loc = #cow;    /* loc = address of cow */
5667
        |или директива             | #resize FALSE
5668
    !   |оператор NOT или смена    |!x_var;  if(!proc())
5669
        |флага операции сравнения. |
5670
   ...  |любое число параметров в  | void proc(...);
5671
   ::   |разрешение видимости      | ::var=0;
5672
Return to contents.
5673
5674
                
5675

5676
5677

5678
	      
5679
            
5680
          
5681
5682
        
5683
      
5684
    
5685
  
5686
5687
5688
5689
5690
5691
5695
5696
5697