Rev 7496 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
7544 | leency | 1 | |
2 | |||
3 |
|
||
4 | |||
5 | |||
6 | body, a, pre { |
||
7 | font-family: 'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace !important; |
||
8 | } |
||
9 | a { |
||
10 | color: #0000ff; |
||
11 | } |
||
12 | #contents a { |
||
13 | font-weight: bold; |
||
14 | text-decoration: none; |
||
15 | } |
||
16 | h1 { |
||
17 | color: #F000F0; |
||
18 | font-size: 200%; |
||
19 | } |
||
20 | h2 { |
||
21 | color: #f00; |
||
22 | font-size: 100%; |
||
23 | margin-bottom: 0; |
||
24 | } |
||
25 | |||
26 | |||
27 | |||
28 |
|
||
29 | |||
30 | |||
31 | 1 Введение. |
||
7496 | leency | 32 | 1.1 История создания и развития. |
7544 | leency | 33 | 1.2 Что такое C--? |
34 | 1.3 Как установить C--. |
||
7496 | leency | 35 | |
7544 | leency | 36 | 2. Управление компиляцией. |
7496 | leency | 37 | 2.1 Параметры командной строки компилятора C--. |
7544 | leency | 38 | 2.1.1 /ON - Оптимизация числовых выражений. |
39 | 2.1.2 /DE - Временное расширение разрядности переменной. |
||
40 | 2.1.3 /ARGC - Альтернативный обработчик командной строки. |
||
41 | 2.1.4 /OST - слияние одинаковых строковых констант. |
||
42 | 2.1.5 /D - установка идентификатора в TRUE из командной строки. |
||
43 | 2.1.6 /IA - упрощенный ввод ассемблерных инструкций. |
||
44 | 2.1.7 /CRI - пропуск повторно включаемого файла. |
||
45 | 2.1.8 /IND - импорт имен процедур из DLL. |
||
46 | 2.1.9 /WS - задать имя stub файла для программ под windows. |
||
47 | 2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции. |
||
48 | 2.1.11 /DBG - создание отладочной информации. |
||
49 | 2.1.12 /J0 /J1 /J2. |
||
50 | 2.1.13 /LST - Создание ассемблерного листинга. |
||
51 | 2.1.14 /ENV - Сохранение адреса переменных окружения. |
||
52 | 2.1.15 /CPA - Очистка post-области данных. |
||
53 | 2.1.16 /W - вывод предупреждений. |
||
54 | 2.1.17 /NW - Выборочное отключение типов предупреждений. |
||
55 | 2.1.18 /WSI - короткая таблица импорта. |
||
56 | 2.2 Директивы транслятора. |
||
57 | 2.2.1 ?ifdef/?ifndef |
||
58 | 2.2.2 ?initallvar |
||
59 | 2.2.3 ?usestartup |
||
60 | 2.2.4 ?startusevar |
||
61 | 2.2.5 ?atexit |
||
62 | 2.2.6 ?startuptomain |
||
63 | 2.2.7 ?undef |
||
64 | 2.2.8 ?align и ?aligncode |
||
65 | 2.2.9 ?pragma |
||
7496 | leency | 66 | |
7544 | leency | 67 | 3. Константы. |
7496 | leency | 68 | 3.1 Числовые константы. |
7544 | leency | 69 | 3.2 Символьные константы. |
70 | 3.3 Строковые константы. |
||
71 | 3.4 Постоянные выражения. |
||
7496 | leency | 72 | |
7544 | leency | 73 | 4. Выражения. |
7496 | leency | 74 | 4.1 Типы выражений. |
7544 | leency | 75 | 4.2 Выражения типа EAX/AX/AL. |
76 | 4.3 Выражения использующие получатель при вычислении выражения. |
||
77 | 4.4 Не - EAX/AX/AL выражения. |
||
78 | 4.5 Условные выражения. |
||
79 | 4.5.1 Простые условные выражения. |
||
80 | 4.5.2 Сложные условные выражения. |
||
81 | 4.6 Изменение типа выражения при присваивании. |
||
82 | 4.7 Вычисление в регистры EAX/AX/AL со знаком. |
||
7496 | leency | 83 | |
7544 | leency | 84 | 5. Идентификаторы. |
7496 | leency | 85 | 5.1 Формат идентификатора. |
7544 | leency | 86 | 5.2 Зарезервированные идентификаторы. |
87 | 5.3 Универсальные регистры для 16 и 32-битного режима. |
||
88 | 5.4 Предопределенные идентификаторы. |
||
7496 | leency | 89 | |
7544 | leency | 90 | 6. Переменные. |
7496 | leency | 91 | 6.1 Типы переменных. |
7544 | leency | 92 | 6.2 Объявление переменных. |
93 | 6.3 Глобальные переменные. |
||
94 | 6.4 Локальные переменные. |
||
95 | 6.5 Динамические переменные и структуры. |
||
96 | 6.6 Присваивание одного значения нескольким переменным. |
||
97 | 6.7 Переменные типа float. |
||
7496 | leency | 98 | 6.7.1 Формат переменных типа float. |
7544 | leency | 99 | 6.7.2 Константы с плавающей точкой. |
100 | 6.7.3 Диапазон допустимых значений. |
||
101 | 6.7.4 Математические операции. |
||
102 | 6.7.5 Преобразования типов. |
||
103 | 6.7.6 Операции сравнения. |
||
104 | 6.7.7 Сравнение переменных типа float с 32-битным регистром. |
||
105 | 6.8 Указатели. |
||
7496 | leency | 106 | |
7544 | leency | 107 | 7. Адресация. |
7496 | leency | 108 | 7.1 Относительная адресация. |
7544 | leency | 109 | 7.2 Абсолютная адресация. |
7496 | leency | 110 | |
7544 | leency | 111 | 8. Работа с блоками данных. |
7496 | leency | 112 | 8.1 Структуры. |
113 | 8.1.1 Что такое структуры. |
||
7544 | leency | 114 | 8.1.2 Синтаксис. |
115 | 8.1.3 Инициализация структур при объявлении. |
||
116 | 8.1.4 Инициализация структуры при выполнении программы. |
||
117 | 8.1.5 Операции с элементами структур. |
||
118 | 8.1.6 Вложенные структуры. |
||
119 | 8.1.7 Отображение тега структуры на блок памяти. |
||
120 | 8.1.8 Битовые поля структур. |
||
121 | 8.2 Объединения. |
||
122 | 8.3 Команды FROM и EXTRACT. |
||
7496 | leency | 123 | |
7544 | leency | 124 | 9. Операторы. |
7496 | leency | 125 | 9.1 Условные инструкции. |
7544 | leency | 126 | 9.2 Циклы do{} while. |
127 | 9.3 Циклы loop, LOOPNZ, loopnz. |
||
128 | 9.4 Цикл while, WHILE. |
||
129 | 9.5 Цикл for, FOR. |
||
130 | 9.6 Оператор переключатель switch. |
||
131 | 9.7 Оператор перехода goto, GOTO. |
||
132 | 9.8 Оператор разрыва break, BREAK. |
||
133 | 9.9 Оператор продолжения continue, CONTINUE. |
||
134 | 9.10 Логическое объединение условий. |
||
135 | 9.11 Переход через циклы. |
||
136 | 9.12 Инвертирование флага проверки условий. |
||
137 | 9.13 Вычисление выражения, а затем проверка условия. |
||
138 | 9.14 Проверка битов при операции сравнения. |
||
139 | 9.15 Оператор перестановки. |
||
140 | 9.16 Оператор отрицания. |
||
141 | 9.17 Оператор инверсии. |
||
142 | 9.18 Специальные условные выражения. |
||
143 | 9.19 Символ $ - вставляет текущий адрес программы. |
||
144 | 9.20 Ключевое слово static и оператор ::. |
||
145 | 9.21 Оператор sizeof. |
||
146 | 9.22 Метки перехода. |
||
7496 | leency | 147 | |
7544 | leency | 148 | 10. Ассемблер. |
7496 | leency | 149 | 10.1 Поддержка команд ассемблера. |
7544 | leency | 150 | 10.2 Ключевое слово asm. |
151 | 10.3 Префикс dup - повторение инструкций DB/DW/DD. |
||
152 | 10.4 Инструкции процессора Pentium III. |
||
7496 | leency | 153 | |
7544 | leency | 154 | 11. Процедуры. |
7496 | leency | 155 | 11.1 Типы процедур, функций и макрокоманд. |
7544 | leency | 156 | 11.2 Стековые процедуры. |
157 | 11.3 Регистровые процедуры. |
||
158 | 11.4 Динамические процедуры. |
||
159 | 11.4.1 Установка динамической процедуры в определенное место программы. |
||
160 | 11.5 inline-процедуры. |
||
161 | 11.5.1 Другое применение inline. |
||
162 | 11.6 Процедуры обработки прерываний. |
||
163 | 11.7 Замена return на goto. |
||
164 | 11.8 Возвращаемые значения. |
||
165 | 11.9 Объявление параметров в регистровых процедурах. |
||
166 | 11.10 Объявление параметров в стековых процедурах. |
||
167 | 11.11 Использование макрокоманд. |
||
168 | 11.12 Передача параметров в стековые процедуры через регистры. |
||
169 | 11.13 Вызов процедур с адресом в регистре. |
||
170 | 11.14 Встоенные в компилятор процедуры. |
||
171 | 11.14.1 Процедуры ABORT, ATEXIT и EXIT. |
||
172 | 11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и |
||
7496 | leency | 173 | outportd. |
7544 | leency | 174 | 11.14.3 Процедуры для работы с вещественными числами. |
175 | 11.15 Классы. |
||
7496 | leency | 176 | 11.15.1 Объявление процедур в структурах. |
7544 | leency | 177 | 11.15.2 Наследование. |
178 | 11.15.3 Наследование процедур. |
||
7496 | leency | 179 | |
7544 | leency | 180 | 12. Типы выходных файлов. |
7496 | leency | 181 | 12.1 Выходные файлы типа COM. |
7544 | leency | 182 | 12.2 Выходные файлы типа EXE. |
183 | 12.3 Выходной файл *.EXE с моделью памяти tiny. |
||
184 | 12.4 Объектный выходной файл OBJ. |
||
185 | 12.5 COM файл symbiosis. |
||
7496 | leency | 186 | 12.5.1 СИМБИОЗ - что это такое? |
7544 | leency | 187 | 12.5.2 Как это делать. |
188 | 12.5.3 Использование. |
||
189 | 12.5.4 Злоупотребления. |
||
190 | 12.6 SYS - драйверы устройств. |
||
191 | 12.7 Компиляция кода расширителей ROM-BIOS. |
||
192 | 12.8 32-битные файлы. |
||
7496 | leency | 193 | 12.8.1 32-битный код под DOS. |
7544 | leency | 194 | 12.8.2 32-битный код под Windows. |
195 | 12.8.3 Вызов API процедур по ординалам. |
||
196 | 12.8.4 Создание DLL под Windows. |
||
197 | 12.8.5 Инициализация DLL при загрузке. |
||
198 | 12.8.6 Компиляция ресурсов. |
||
199 | 12.9 Выходные файлы для MeOS. |
||
7496 | leency | 200 | |
7544 | leency | 201 | 13. Приложения. |
7496 | leency | 202 | 13.1 Поиск включаемых файлов. |
7544 | leency | 203 | 13.2 Регистры, которые должны быть сохранены. |
204 | 13.3 C--.ini файл. |
||
205 | 13.4 startup.h-- файл. |
||
206 | 13.5 mainlib.ldp файл. |
||
207 | 13.6 C-- символы. |
||
208 | |||
7496 | leency | 209 | |
210 | |||
7544 | leency | 211 |
|
7496 | leency | 212 | |
213 | 1.1 История создания и развития. |
||
7544 | leency | 214 | |
7496 | leency | 215 | |
216 | Автором языка SPHINX C-- является Peter Cellik (CANADA). Последняя |
||
217 | авторская версия SPHINX C-- v0.203 от 28.Oct.96. К сожалению автор |
||
218 | отказался от дальнейшего развития языка. С 1998 года, уже почти умерший |
||
219 | проект, подхватил Михаил Шекер (Россия). Изначально компилятор был freeware |
||
220 | (и даже greenware, как его называл Peter Cellik). Таким статус компилятора |
||
221 | остался и поныне. |
||
222 | |||
223 | Первоначально компилятор мог создавать только *.com файлы и был |
||
224 | рассчитан на создание небольших demo-программ и резидентов (TSR). В |
||
225 | дальнейшем возможности компилятора расширялись, так как этого требовало |
||
226 | наше бурное время. |
||
227 | |||
228 | При развитии компилятора, было стремление придерживаться следующих |
||
229 | принципов: |
||
230 | |||
231 | 1. Максимально возможная совместимость синтаксиса с последней версией |
||
232 | компилятора написанного Peter Cellik. Это давало возможность с минимальными |
||
233 | затратами (а чаще всего без всяких затрат) адаптировать программы, |
||
234 | написанные для 0.203 версии компилятора, к последней на этот момент версии |
||
235 | компилятора. |
||
236 | |||
237 | 2. Сблизить синтаксис компилятора со стандартным языком C. Это могло |
||
238 | значительно облегчить перенос программ написанных на C. |
||
239 | |||
240 | 3. Также прилагались усилия, для того, чтобы человек знающий только |
||
241 | ассемблер мог бы с минимальными затратами освоить C--. |
||
242 | |||
243 | Вот эти, зачастую противоречащие друг другу принципы, влияли на выбор |
||
244 | реализации возможностей компилятора. Насколько это удалось - судить Вам. |
||
245 | |||
246 | Если у Вас есть предложения и идеи по улучшению компилятора - пишите. |
||
247 | Мой e-mail sheker@mail.ru . Я с удовольствием выслушаю Ваши предложения, но |
||
248 | не гарантирую, что все они будут реализованы. Если реализовывать все |
||
249 | поступающие предложения, то компилятор превратится в свалку. Но если Ваше |
||
250 | предложение будет ценным (на мой взгляд, так что Вам придется свое |
||
251 | предложение хорошо аргументировать) и его будет возможным реализовать, оно |
||
252 | без сомнения найдет место в компиляторе. |
||
7544 | leency | 253 | Return to contents. |
7496 | leency | 254 | |
255 | |||
7544 | leency | 256 | |
7496 | leency | 257 | 1.2 Что такое C--? |
7544 | leency | 258 | |
7496 | leency | 259 | |
260 | C-- был разработан, для того чтобы строить маленькие и быстрые |
||
261 | программы. Это наиболее подходит для создания резидентных программ (TSR), |
||
262 | программ, требующих обработку прерываний или программ у которых ограничены |
||
263 | ресурсы. |
||
264 | |||
265 | C-- занимает промежуточное положение между си и ассемблером. В связи с |
||
266 | этим промежуточным положением, Вам, для того чтобы писать программы на C--, |
||
267 | необходимо знать и ассемблер и си. Если Вам надоело возиться с огромными |
||
268 | ассемблерными листингами, а излишняя строгость языка C Вас угнетает, то этот |
||
269 | язык для ВАС. |
||
270 | |||
271 | Сейчас компилятор C-- может создавать 32-битные программы под Windows |
||
272 | (EXE-файлы формата PE) и 32-битные программы под DOS (LE-формат). Имеет |
||
273 | встроенный компилятор ресурсов и дизассемблер для генерации листинга |
||
274 | откомпилированного файла. Поддерживает ассемблерные инструкции процессора |
||
275 | Pentium III и ассемблерные инструкции FPU. Компилятор может генерировать |
||
276 | отладочную информацию совместимую с отладчиками фирмы Borland. Компилятор |
||
277 | может создавать объектные файлы (obj), но только для DOS программ. |
||
278 | |||
279 | C-- разработан только для использования на компьютерах с процессорами |
||
280 | совместимыми с семейством 80x86. Компилятор может работать только с |
||
281 | операционными системами DOS и семейством Windows. |
||
7544 | leency | 282 | Return to contents. |
7496 | leency | 283 | |
284 | |||
7544 | leency | 285 | |
7496 | leency | 286 | 1.3 Как установить C--. |
7544 | leency | 287 | |
7496 | leency | 288 | |
289 | Компилятору C-- для работы нужны совсем незначительные ресурсы: |
||
290 | процессор 386 или лучше, чуть более 1 Мб дискового пространства и 4Мб |
||
291 | оперативной памяти. Компилятор может быть установлен на компьютеры с |
||
292 | операционной системой Windows 95 или лучше. Компилятор также может работать |
||
293 | в среде чистого DOS. В основном пакете компилятора находится 32-битная DOS |
||
294 | версия компилятора. На сайте http://sheker.chat.ru или |
||
295 | http://c--sphinx.narod.ru можно найти и консольную версию компилятора. |
||
296 | Консольная версия компилятора может работать только в среде Windows, но |
||
297 | она, в отличие от DOS версии, может работать с длинными именами исходных |
||
298 | файлов. |
||
299 | |||
300 | Установить компилятор C-- на Ваш компьютер очень просто. Предположим, |
||
301 | что Вы решили установить C-- на диск C. Создайте на диске C директорию |
||
302 | (папку) с именем C-- или с другим, удобным и понятным для Вас именем |
||
303 | (например, ДОСовской командой: MD C-- или другим доступным Вам способом). |
||
304 | Затем с сайта http://sheker.chat.ru или http://c--sphinx.narod.ru скачайте |
||
305 | файлы full_c--.zip и ful_c--2.zip и разархивируйте их в этой директории. |
||
306 | Затем в файле autoexec.bat можно прописать путь к директории с |
||
307 | компилятором. И все. Компилятор готов к работе. Если Вы добавляли путь к |
||
308 | компилятору в файл autoexec.bat, то Вам придется перегрузить операционную |
||
309 | систему. |
||
310 | |||
311 | Переменная окружения для компилятора C-- задается либо из командной |
||
312 | строки либо из командного файла (лучше всего ее прописать в autoexec.bat). |
||
313 | Эта переменная должна указывать компилятору, где находятся его библиотечные |
||
314 | файлы. Пример: |
||
315 | |||
316 | set C--=c:\c--\lib |
||
317 | |||
318 | Большой необходимости в переменной окружения для сегодняшней версии |
||
319 | компилятора нет. Существует несколько других способов, указать компилятору |
||
320 | место расположения библиотек. Поэтому определять или не определять |
||
321 | переменную окружения дело вашего вкуса и привычек. |
||
7544 | leency | 322 | Return to contents. |
7496 | leency | 323 | |
324 | |||
7544 | leency | 325 | |
7496 | leency | 326 | 2. Управление компиляцией. |
327 | |||
328 | 2.1 Параметры командной строки компилятора C--. |
||
7544 | leency | 329 | |
7496 | leency | 330 | |
331 | Формат командной строки вызова компилятора C--: |
||
332 | |||
333 | C-- [Параметры] [ИМЯ INI ФАЙЛА] [ИМЯ ИСХОДНОГО ФАЙЛА] |
||
334 | |||
335 | Имя исходного файла можно задавать без расширения. Компилятор ищет |
||
336 | файл с расширением c--, cmm, c. |
||
337 | |||
338 | Параметры выделяются предшествующим символом / или -. |
||
339 | Инвертировать функцию опции можно завершающим символом -. |
||
340 | |||
341 | Список поддерживаемых параметров: |
||
342 | |||
343 | /0 использовать только команды 8086/8088 процессора (установлено |
||
344 | по умолчанию при компиляции 16-битных программ). |
||
345 | /1 использовать команды 80186 процессора. |
||
346 | /2 использовать команды и оптимизацию для 80286 процессора. |
||
347 | /3 использовать команды и оптимизацию для 80386 процессора. |
||
348 | (установлено по умолчанию для 32-битных программ). |
||
349 | /4 использовать команды и оптимизацию для 80486 процессора. |
||
350 | /5 использовать команды и оптимизацию для Pentium процессора. |
||
351 | /6 использовать команды и оптимизацию для Pentium MMX процессора. |
||
352 | /7 использовать команды и оптимизацию для Pentium Pro процессора. |
||
353 | /8 использовать команды и оптимизацию для Pentium II процессора. |
||
354 | /9 использовать команды и оптимизацию для Pentium III процессора |
||
355 | (пока не реализовано из-за отсутствии информации). |
||
356 | /A выравнивание данных на четный адрес |
||
357 | по умолчанию разрешено, поддерживает инверсию |
||
358 | /AC выравнивание адреса начала циклов |
||
359 | по умолчанию отключено, поддерживает инверсию |
||
360 | имеет смысл только на процессорах Pentium+ |
||
361 | /AL=## установить значение байта заполнения при выравнивании данных |
||
362 | по умолчанию 0. |
||
363 | /AP выравнивание адреса начала процедур. |
||
364 | по умолчанию отключено, поддерживает инверсию |
||
365 | имеет смысл только на процессорах Pentium и лучше |
||
366 | /ARGC вставить блок разбора командной строки |
||
367 | по умолчанию отключено, поддерживает инверсию |
||
368 | /AS выравнивание в структурах. |
||
369 | по умолчанию отключено, поддерживает инверсию |
||
370 | /AT вставить блок поддержки ATEXIT процедуры |
||
371 | по умолчанию отключено, поддерживает инверсию |
||
372 | /C вставить блок игнорирования CTRL-C |
||
373 | по умолчанию отключен, поддерживает инверсию |
||
374 | имеет смысл только под DOS программы |
||
375 | /CRI проверять включаемые файлы на повторную загрузку |
||
376 | по умолчанию включено, поддерживает инверсию |
||
377 | /CPA очистка post-области данных |
||
378 | /D32 создать EXE файл (32 битный код под DOS) |
||
379 | по умолчанию COM |
||
380 | /D=idname определить идентификатор для условной компиляции |
||
381 | по умолчанию нет |
||
382 | /DBG генерировать отладочную информацию |
||
383 | по умолчанию нет |
||
384 | /DE временное расширение разрядности после умножения |
||
385 | по умолчанию отключено, поддерживает инверсию |
||
386 | /DLL создать DLL для Windows32 |
||
387 | по умолчанию COM |
||
388 | /ENV сохранение адреса переменных окружения |
||
389 | /EXE создать EXE файл для DOS (модель SMALL) |
||
390 | по умолчанию COM |
||
391 | /HELP /H /? справка, эта информация |
||
392 | /IA имена ассемблерных инструкций являются идентификаторами |
||
393 | по умолчанию отключено, поддерживает инверсию |
||
394 | /IND=name импорт имен из файла name. |
||
395 | /IP=path задать путь поиска включаемых файлов |
||
396 | по умолчанию нет |
||
397 | /IV инициализировать все переменные |
||
398 | по умолчанию отключено, поддерживает инверсию |
||
399 | /J0 не делать начальный jump на main() |
||
400 | по умолчанию отключено, поддерживает инверсию |
||
401 | В COM-файлах не создает jmp на main. В остальных не создается |
||
402 | блок начальной инициализации программы, а управление |
||
403 | передается сразу на main. |
||
404 | /J1 делать короткий jump на main() |
||
405 | по умолчанию нет |
||
406 | имеет смысл только в COM-файлах |
||
407 | /J2 делать jump на main() |
||
408 | по умолчанию да, поддерживает инверсию |
||
409 | имеет смысл только в COM-файлах |
||
410 | /LAI список поддерживаемых ассемблерных инструкций |
||
411 | /LRS загружать числовые константы через стек. |
||
412 | по умолчанию да, поддерживает инверсию |
||
413 | /LST создать ассемблерный листинг |
||
414 | /ME показать мой адрес и имя |
||
415 | /MEOS создать исполняемый файл для MeOS |
||
416 | по умолчанию COM |
||
417 | /MER=## установить максимальное число ошибок |
||
418 | по умолчанию 16 |
||
419 | /MIF=file определить имя главного компилируемого файла |
||
420 | /NS запретить подключать stub файлов |
||
421 | по умолчанию нет, поддерживает инверсию |
||
422 | /NW=## выборочное отключение предупреждений |
||
423 | /OBJ создать OBJ файл |
||
424 | только 16 битный код. |
||
425 | по умолчанию COM |
||
426 | /OC оптимизировать по размеру кода |
||
427 | по умолчанию нет, поддерживает инверсию |
||
428 | /ON оптимизация чисел |
||
429 | по умолчанию нет, поддерживает инверсию |
||
430 | /OS оптимизация по скорости выполнения |
||
431 | по умолчанию да, поддерживает инверсию |
||
432 | /OST оптимизация строковых идентификаторов |
||
433 | по умолчанию отключено, поддерживает инверсию |
||
434 | /P вставить блок разборки командной строки |
||
435 | по умолчанию нет, поддерживает инверсию |
||
436 | /R вставить блок уменьшающий размер доступной памяти. |
||
437 | по умолчанию да, поддерживает инверсию |
||
438 | имеет смысл только в DOS-файлах |
||
439 | /S=##### установить размер стека |
||
440 | по умолчанию 2048 |
||
441 | /SA=#### начальное смещение адреса запуска программы |
||
442 | имеет смысл только в COM-файлах, по умолчанию 0x100 |
||
443 | /SOBJ создать ведомый OBJ файл |
||
444 | по умолчанию COM |
||
445 | /STM перенести блок startup кода в процедуру main |
||
446 | по умолчанию нет, поддерживает инверсию |
||
447 | имеет смысл только в COM-файлах |
||
448 | /SUV=#### начальный адрес не инициализированных переменных, при |
||
449 | использовании ими startup кода. |
||
450 | имеет смысл только в COM-файлах, по умолчанию равен /SA |
||
451 | /SYM надстройка для COM файла |
||
452 | по умолчанию COM |
||
453 | /SYS создать драйвер устройств (SYS) |
||
454 | по умолчанию COM |
||
455 | /TEXE создать EXE файл для DOS (модель TINY) |
||
456 | по умолчанию COM |
||
457 | /UL использовать lea при оптимизации сложения регистров. |
||
458 | по умолчанию да, поддерживает инверсию |
||
459 | /UST использовать startup код для переменных. |
||
460 | имеет смысл только в COM-файлах |
||
461 | по умолчанию нет, поддерживает инверсию |
||
462 | /W разрешить предупреждения |
||
463 | по умолчанию нет, поддерживает инверсию |
||
464 | /W32 создать EXE файл для Windows32 GUI |
||
465 | по умолчанию COM |
||
466 | /W32C создать EXE файл для Windows32 console |
||
467 | по умолчанию COM |
||
468 | /WBSS помещать не инициализированные данные в отдельную секцию. |
||
469 | по умолчанию для /w32 разрешено, для остальных запрещено. |
||
470 | поддерживает инверсию |
||
471 | /WF=file перенаправить вывод предупреждений в файл. |
||
472 | по умолчанию нет |
||
473 | /WFA использовать быстрые вызовы API процедур |
||
474 | по умолчанию да, поддерживает инверсию |
||
475 | только под windows |
||
476 | /WFU создавать таблицу перемещений (для Windows32) |
||
477 | по умолчанию нет, поддерживает инверсию |
||
478 | только под windows |
||
479 | для DLL устанавливается в да |
||
480 | /WIB=##### установить адрес image base |
||
481 | по умолчанию 0x400000 |
||
482 | /WMB создавать Windows-файл с единым блоком |
||
483 | по умолчанию да, поддерживает инверсию |
||
484 | только под windows |
||
485 | для DLL устанавливается в нет |
||
486 | /WORDS выдать список зарезервированных идентификаторов |
||
487 | /WS=name указывает имя файла используемого в качестве stub под windows. |
||
488 | /X запретить вставлять в код SPHINXC-- сигнатуру |
||
489 | по умолчанию разрешено, поддерживает инверсию |
||
490 | отключается если есть J0 |
||
491 | |||
492 | Примечание: выражение поддерживает инверсию означает, что для данной |
||
493 | опции можно использовать и противоположное значение с помощью символа - |
||
494 | после опции. Пример: |
||
495 | |||
496 | /WFA- |
||
497 | |||
498 | Параметры командной строки можно писать как большими, так и |
||
499 | маленькими буквами. |
||
7544 | leency | 500 | Return to contents. |
7496 | leency | 501 | |
502 | |||
7544 | leency | 503 | |
7496 | leency | 504 | 2.1.1 /ON - Оптимизация числовых выражений. |
7544 | leency | 505 | |
7496 | leency | 506 | |
507 | При включении в командную строку опции /ON или в файл C--.INI строчки |
||
508 | ON, компилятор будет анализировать операции над числами и где это |
||
509 | можно, сокращать число операций. Например: |
||
510 | |||
511 | Строка до оптимизации | После оптимизации |
||
512 | ----------------------------------------------- |
||
513 | AX = var + 7 - 3; | AX = var + 4; |
||
514 | AX = var * 2 * 5; | AX = var * 10; |
||
515 | AX = var * 2 / 4; | AX = var / 2; |
||
516 | AX = var * 10 / 2; | AX = var * 5; |
||
517 | AX = var / 2 / 3; | AX = var / 6; |
||
518 | AX = var / 4 * 8; | AX = var * 2; |
||
519 | AX = var / 16 * 16; | AX = var; |
||
520 | |||
521 | Возможные отрицательные последствия: |
||
522 | Применение этой оптимизации может иметь и негативные последствия. |
||
523 | Например, если Вам нужно выровнять значение переменной на границу |
||
524 | параграфа, Вы напишите строку: |
||
525 | |||
526 | var = var / 16 * 16; |
||
527 | |||
528 | но после оптимизации будет |
||
529 | |||
530 | var = var; |
||
531 | |||
532 | т.е. выравнивание не будет произведено. Этого можно избежать, если |
||
533 | разбить это выражение на два: |
||
534 | |||
535 | var = var / 16; |
||
536 | var = var * 16; |
||
537 | |||
538 | тогда оптимизация не будет произведена. Но для получения более |
||
539 | компактного кода лучше будет записать так: |
||
540 | |||
541 | AX = var; |
||
542 | AX = AX / 16; |
||
543 | AX = AX * 16; |
||
544 | var = AX; |
||
7544 | leency | 545 | Return to contents. |
7496 | leency | 546 | |
547 | |||
7544 | leency | 548 | |
7496 | leency | 549 | 2.1.2 /DE - Временное расширение разрядности переменной. |
7544 | leency | 550 | |
7496 | leency | 551 | |
552 | Как известно, после умножения может произойти переполнение, т.е |
||
553 | разрядность результата может превысить разрядность исходных операндов и |
||
554 | произойдет искажение результата. Частично решить эту проблему Вам поможет |
||
555 | опция командной строки /DE или строка DE в файле C--.INI. После команды |
||
556 | умножения компилятор будет просматривать остаток строки и если обнаружит, |
||
557 | что расширение разрядности может быть востребовано (востребовать |
||
558 | расширенную разрядность могут операции деления и вычисления остатка), то |
||
559 | будут приняты меры по ее сохранению. Например: |
||
560 | |||
561 | a = b*c+d/e; //здесь будет включена поддержка расширения разрядности |
||
562 | a = b*c+d*e; //здесь поддержки расширения разрядности не будет. |
||
563 | |||
564 | Однако применение этой опции может иметь и негативные последствия. |
||
565 | Покажу это на примере: |
||
566 | |||
567 | пусть имеется выражение |
||
568 | |||
569 | a = b * c / d; |
||
570 | |||
571 | если значения переменных b = 0xC000, c = 0x1000, d=0x10, после запуска |
||
572 | такая программа зависнет с сообщением о том, что произошло переполнение |
||
573 | при делении. |
||
7544 | leency | 574 | Return to contents. |
7496 | leency | 575 | |
576 | |||
7544 | leency | 577 | |
7496 | leency | 578 | 2.1.3 /ARGC - Альтернативный обработчик командной строки. |
7544 | leency | 579 | |
7496 | leency | 580 | |
581 | Отличие этого обработчика командной строки от parsecommandline |
||
582 | заключается в том, что при вызове PARAMSTR(0); Вы получите адрес строки в |
||
583 | которой указан путь и имя запущенной программы. Следующие вызовы этой |
||
584 | процедуры с увеличивающимся параметром будут возвращать адреса слов |
||
585 | командной строки. А вызов процедуры PARAMCOUNT вернет Вам число слов в |
||
586 | командной строке плюс один. |
||
587 | |||
588 | Альтернативный обработчик командной строки включается директивой |
||
589 | ?argc TRUE или из командной строки компилятора ключом /argc или |
||
590 | строчкой argc в файле C--.INI. |
||
7544 | leency | 591 | Return to contents. |
7496 | leency | 592 | |
593 | |||
7544 | leency | 594 | |
7496 | leency | 595 | 2.1.4 /OST - слияние одинаковых строковых констант. |
7544 | leency | 596 | |
7496 | leency | 597 | |
598 | Если этот режим оптимизации будет активизирован, то компилятор будет |
||
599 | запоминать все строковые константы и при обнаружении одинаковых в код |
||
600 | файла не будет вставлена повторная строковая константа, а будет сделана |
||
601 | ссылка на первую, обнаруженную ранее строковую константу. В оптимизации |
||
602 | участвуют только неименованные строковые константы. Т.е. если массив или |
||
603 | структура будет инициализированы строкой, то такая строка не будет |
||
604 | участвовать в процессе инициализации, так эта строка может быть изменена |
||
605 | в процессе работы программы. Пример: |
||
606 | |||
607 | char var="test"; //эта строка не будет участвовать в процессе |
||
608 | //оптимизации. |
||
609 | |||
610 | void proc(){ |
||
7544 | leency | 611 | WRITESTR("test"); // эта строка будет участвовать в оптимизации. |
7496 | leency | 612 | AX="test"; // переменной AX будет присвоен адрес строки, |
613 | // которая была вставлена в код программы в |
||
614 | // предыдущей строке. |
||
615 | } |
||
616 | |||
617 | Обо всех случаях обнаружения повторной строки компилятор будет |
||
618 | выдавать предупреждения. |
||
619 | |||
620 | Включается этот режим оптимизации либо с командной строки /ost, либо |
||
621 | директивой #pragma option ost, либо строкой в файле c--.ini - ost. |
||
622 | Отключить, включенный ранее, этот режим можно директивой #pragma option ost-. |
||
7544 | leency | 623 | Return to contents. |
7496 | leency | 624 | |
625 | |||
7544 | leency | 626 | |
7496 | leency | 627 | 2.1.5 /D - установка идентификатора в TRUE из командной строки. |
7544 | leency | 628 | |
7496 | leency | 629 | |
630 | Если Вы написали программу, которая может компилироваться по разному, |
||
631 | в зависимости от состояния некоторых идентификаторов (используется режим |
||
632 | условной компиляции), то Вам очень может пригодится эта опция. |
||
633 | Устанавливая с командной строки различные идентификаторы, Вы можете |
||
634 | получать различные варианты программы, не редактируя исходный текст |
||
635 | программы. |
||
636 | |||
637 | Идентификатор вводится с командной строки ключом /d=idname. |
||
7544 | leency | 638 | Return to contents. |
7496 | leency | 639 | |
640 | |||
7544 | leency | 641 | |
7496 | leency | 642 | 2.1.6 /IA - упрощенный ввод ассемблерных инструкций. |
7544 | leency | 643 | |
7496 | leency | 644 | |
645 | Стало возможным использовать ассемблерные инструкции без префикса $ |
||
646 | и вне блока asm. Этот режим включается: с командной строки опцией /ia; |
||
647 | в файле конфигурации строкой ia или директивой #pragma option ia. |
||
648 | |||
649 | Когда этот режим включен, все имена ассемблерных инструкций становятся |
||
650 | зарезервированными словами, т.е. Вы не сможете эти имена использовать в |
||
651 | качестве имен переменных или процедур. Ассемблерные инструкции компилятор |
||
652 | распознает независимо от того, написаны они маленькими или большими |
||
653 | буквами. |
||
7544 | leency | 654 | Return to contents. |
7496 | leency | 655 | |
656 | |||
7544 | leency | 657 | |
7496 | leency | 658 | 2.1.7 /CRI - пропуск повторно включаемого файла. |
7544 | leency | 659 | |
7496 | leency | 660 | |
661 | Чаще всего, повторно включать файл в компилируемый проект, нет |
||
662 | необходимости, но это иногда происходит из-за того, что некоторые |
||
663 | включаемые файлы сами включают другие файлы. Чтобы этого не происходило |
||
664 | приходится делать проверку на повторную загрузку файла. Теперь эту |
||
665 | функцию берет на себя компилятор и у Вас отпадает необходимость делать |
||
666 | эту проверку. |
||
667 | |||
668 | Но иногда (очень редко) возникает потребность сделать повторное |
||
669 | включение файла. Для этого в компиляторе есть опция командной строки |
||
670 | /cri-, которая запрещает компилятору делать проверку на повторное |
||
671 | включение. Соответственно, для c--.ini файла, это можно сделать строкой |
||
672 | cri- или директивой в компилируемом файле - #pragma option cri-. |
||
7544 | leency | 673 | Return to contents. |
7496 | leency | 674 | |
675 | |||
7544 | leency | 676 | |
7496 | leency | 677 | 2.1.8 /IND - импорт имен процедур из DLL. |
7544 | leency | 678 | |
7496 | leency | 679 | |
680 | Если Вы хотите в своей программе использовать DLL, для которой нет |
||
681 | заголовочного файла с описанием процедур, то компилятор может |
||
682 | импортировать имена из этой DLL. Для этого Вам надо указать имя этой |
||
683 | библиотеки либо через опцию командной строки /ind=name.dll, либо в |
||
684 | файле INI строкой 'ind=name.dll', либо через директиву '#pragma option |
||
685 | ind=name.dll'. |
||
686 | |||
687 | К недостатком такого способа получения имен можно отнести то, что при |
||
688 | компиляции программы библиотека, из которой импортируются имена, |
||
689 | обязательно должна присутствовать в компьютере. Также, если имена в |
||
690 | библиотеке написаны без суффикса '@number', компилятор не будет |
||
691 | контролировать число параметров передаваемых процедуре. И, к сожалению, |
||
692 | компилятор умеет импортировать имена из библиотек имеющих только формат |
||
693 | PE-файла. |
||
7544 | leency | 694 | Return to contents. |
7496 | leency | 695 | |
696 | |||
7544 | leency | 697 | |
7496 | leency | 698 | 2.1.9 /WS - задать имя stub файла для программ под windows. |
7544 | leency | 699 | |
7496 | leency | 700 | |
701 | Как известно, в программах под windows есть DOS заглушка, называемая |
||
702 | stub, которой передается управление при запуске такой программы в чистом |
||
703 | DOS-е. Обычно такая заглушка выводит на экран сообщение о том, что эту |
||
704 | программу надо запускать в среде windows. |
||
705 | |||
706 | Вы можете вместо стандартного stub использовать свой. Для этого Вам |
||
707 | необходимо указать имя 16-битного EXE-файла либо через опцию командной |
||
708 | строки /ws=filename, либо строкой в INI-файле ws=filename, либо |
||
709 | директивой #pragma option ws=filename. |
||
710 | |||
711 | Таким образом, у Вас появилась возможность создавать программы, |
||
712 | работающие и под DOS и под windows. |
||
7544 | leency | 713 | Return to contents. |
7496 | leency | 714 | |
715 | |||
7544 | leency | 716 | |
7496 | leency | 717 | 2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции. |
7544 | leency | 718 | |
7496 | leency | 719 | |
720 | Секция .bss создается автоматически при компиляции программ с ключом |
||
721 | /w32. Если Вы хотите иметь эту секцию и при компиляции программ с |
||
722 | ключами /w32c или /dll Вам необходимо добавить либо в командной |
||
723 | строке опцию /wbss, либо строку wbss в INI-файле, либо директиву |
||
724 | #pragma option wbss. |
||
725 | |||
726 | Использование секции .bss практически не влияет на размер получаемого |
||
727 | файла. Теоретически, для процессоров, у которых есть отдельный кэш для |
||
728 | данных, использование секции .bss, должно повышать скорость работы |
||
729 | программы. |
||
7544 | leency | 730 | Return to contents. |
7496 | leency | 731 | |
732 | |||
7544 | leency | 733 | |
7496 | leency | 734 | 2.1.11 /DBG - создание отладочной информации. |
7544 | leency | 735 | |
7496 | leency | 736 | |
737 | Если при компиляции программы в командную строку добавить ключ /dbg, |
||
738 | или в файл конфигурации c--.ini добавить строку dbg, то компилятор после |
||
739 | окончания компиляции создаст файл с отладочной информацией. Этот файл |
||
740 | имеет имя главного модуля и имеет расширение *.tds. |
||
741 | |||
742 | Отладочная информация создаваемая компилятором C-- совместима с |
||
743 | отладочной информацией создаваемой компиляторами фирмы Borland. Но, пока, |
||
744 | эта информация реализована еще не в полном объеме. Создаваемой сейчас |
||
745 | отладочной информации достаточно для проведения простейшей отладки |
||
746 | программы. |
||
747 | |||
748 | Для 16-битных программ под DOS для отладки надо использовать Turbo |
||
749 | Debugger из пакета Borland C v4.5 или лучше (файл td.exe). |
||
750 | |||
751 | Для программ под Windows надо использовать 32-битный отладчик из этого |
||
752 | же пакета (файл td32.exe). |
||
753 | |||
754 | Для 32-битных программ, использующих расширитель DOS применять для |
||
755 | отладки Turbo Debugger невозможно. Но, может быть я не знаю, как это |
||
756 | делать. Если Вы знаете, как создавать 32-битные программы с |
||
757 | DOS-расширителем компиляторами фирмы Borland с включением в них отладочной |
||
758 | информации, то расскажите мне. А я попробую применить это для C--. |
||
7544 | leency | 759 | Return to contents. |
7496 | leency | 760 | |
761 | |||
7544 | leency | 762 | |
7496 | leency | 763 | 2.1.12 /J0 /J1 /J2 |
7544 | leency | 764 | |
7496 | leency | 765 | |
766 | Синонимом ключей /J0 /J1 /J2 является директива #jumptomain с |
||
767 | параметрами NONE, SHORT и NEAR соответственно. |
||
768 | |||
769 | Директива #jumptomain выполняет немного различные функции в |
||
770 | зависимости от типа выходного файла. |
||
771 | |||
772 | Компиляция файла типа *.com и *.exe модель памяти tiny: |
||
773 | |||
774 | #jumptomain NONE (-j0) - в этом случае по окончании кода начальной |
||
775 | инициализации программы не генерируется jmp на процедуру main. Эту |
||
776 | директиву следует использовать в случае, если до процедуры main нет других |
||
777 | не динамических процедур и инициализированных переменных. |
||
778 | |||
779 | #jumptomain SHORT (-j1) - в этом случае по окончании кода начальной |
||
780 | инициализации генерируется короткий jmp на процедуру main. Эту директиву |
||
781 | следует использовать, если до процедуры main находится не более 128 байт |
||
782 | кода и данных. |
||
783 | |||
784 | #jumptomain NEAR (-j2) - это состояние устанавливается по умолчанию. При |
||
785 | этом генерируется близкий jmp на процедуру main. |
||
786 | |||
787 | Компиляция файлов *.exe (ключи -exe -d32 -w32 -w32c): |
||
788 | |||
789 | #jumptomain NONE (-j0) - в этом случае код начальной инициализации |
||
790 | программы не генерируется и управление при запуске передается сразу на |
||
791 | процедуру main. |
||
792 | |||
793 | Во всех остальных случаях генерируется код начальной инициализации и |
||
794 | управление на процедуру main передается инструкцией call. |
||
795 | |||
796 | Компиляция файлов *.dll: |
||
797 | |||
798 | #jumptomain NONE (-j0) - в этом случае код начальной инициализации |
||
799 | программы не генерируется и управление при запуске передается сразу на |
||
800 | процедуру main. |
||
801 | |||
802 | Во всех остальных случаях генерируется код заглушки и управление на |
||
803 | процедуру main не передается. Фактически процедура main в этом случае не |
||
804 | нужна. |
||
805 | |||
806 | Процедура main при создании файлов DLL должна выглядеть немного иначе, |
||
807 | чем в других случаях: |
||
808 | |||
809 | dword main ( dword hInstDLL, reason, reserv ) |
||
810 | { |
||
811 | ... |
||
812 | } |
||
7544 | leency | 813 | Return to contents. |
7496 | leency | 814 | |
815 | |||
7544 | leency | 816 | |
7496 | leency | 817 | 2.1.13 /LST - Создание ассемблерного листинга. |
7544 | leency | 818 | |
7496 | leency | 819 | |
820 | С помощью дополнительной опции командной строки -lst Вы можете |
||
821 | получить вместе с исполнительным файлом и его ассемблерный листинг. |
||
822 | Листинг будет помещен в файл одноименный с исполнительным файлом и |
||
823 | имеющим расширение *.lst. |
||
824 | |||
825 | Ассемблерный листинг создается независимой от компилятора частью кода |
||
826 | с использованием информации накапливаемой при компиляции программы. |
||
7544 | leency | 827 | Return to contents. |
7496 | leency | 828 | |
829 | |||
7544 | leency | 830 | |
7496 | leency | 831 | 2.1.14 /ENV - Сохранение адреса переменных окружения. |
7544 | leency | 832 | |
7496 | leency | 833 | |
834 | Если при компиляции программы Вы в командную строку добавите опцию |
||
835 | -ENV или в файл c--.ini строка ENV, то компилятор добавит в вашу |
||
836 | программу переменную environ, в которой при загрузке будет сохранятся |
||
837 | адрес переменных окружения запускаемой программы. Для программ под |
||
838 | Windows это будет полный адрес, а для остальных в этой переменной будет |
||
839 | сохраняться только адрес сегмента. |
||
7544 | leency | 840 | Return to contents. |
7496 | leency | 841 | |
842 | |||
7544 | leency | 843 | |
7496 | leency | 844 | 2.1.15 /CPA - Очистка post-области данных. |
7544 | leency | 845 | |
7496 | leency | 846 | |
847 | Переменные, которым в теле программы не было присвоено никакое |
||
848 | значение, не включаются в тело скомпилированной программы. Для них |
||
849 | резервируется память за пределами программы. Но эта память может быть |
||
850 | заполнена произвольной информацией. |
||
851 | |||
852 | Если Вам необходимо, чтобы неинициализированные переменные при |
||
853 | загрузке программы всегда содержали одно и тоже значение (ноль) - |
||
854 | включите в командную строку опцию -CPA. |
||
7544 | leency | 855 | Return to contents. |
7496 | leency | 856 | |
857 | |||
7544 | leency | 858 | |
7496 | leency | 859 | 2.1.16 /W - вывод предупреждений. |
7544 | leency | 860 | |
7496 | leency | 861 | |
862 | По умолчанию компилятор не выводит предупреждения и многие даже не |
||
863 | подозревают о существовании такой полезной опции. В C-- предупреждения |
||
864 | фактически являются подсказками для создания оптимальных программ и |
||
865 | зачастую облегчают отладку программ. В предупреждениях компилятор может |
||
866 | сообщить Вам о том, в каком месте можно использовать короткие формы |
||
867 | операторов IF, WHILE, FOR... О том, какие процедуры, переменные и |
||
868 | структуры определенные в вашей программе не были использованы. О том |
||
869 | какие регистры компилятор использовал без вашего ведома и много другой |
||
870 | полезной информации. |
||
871 | |||
872 | По умолчанию предупреждения выводятся на экран. Но их бывает так |
||
873 | много, что они могут не поместиться на экране. Поэтому в компиляторе есть |
||
874 | опция, по которой все предупреждения выводятся в файл. Имя этого файла |
||
875 | задается в той же опции. Поместив в свой c--.ini файл пару вот этих строк: |
||
876 | |||
877 | w |
||
878 | wf=warning |
||
879 | |||
880 | Вы будете получать в файле warning предупреждения. |
||
7544 | leency | 881 | Return to contents. |
7496 | leency | 882 | |
883 | |||
7544 | leency | 884 | |
7496 | leency | 885 | 2.1.17 /NW - Выборочное отключение типов предупреждений. |
7544 | leency | 886 | |
7496 | leency | 887 | |
888 | Сейчас компилятор может выдавать 12 типов предупреждений и, иногда их |
||
889 | бывает так много, что становится трудно в них ориентироваться. Теперь |
||
890 | можно выборочно запрещать выдачу предупреждений. Для этого в командной |
||
891 | строке (или в файле C--.INI) можно установить опцию /nw=number, где |
||
892 | number - число от 1 до 12. Этим цифрам соответствуют следующие типы |
||
893 | предупреждений: |
||
894 | |||
895 | 1 - Optimize numerical expressions |
||
896 | 2 - Compiler used register ..." |
||
897 | 3 - Short operator '...' may be used |
||
898 | 4 - String '...' repeated |
||
899 | 5 - Expansion variable |
||
900 | 6 - Signed value returned |
||
901 | 7 - '...' defined above, therefore skipped. |
||
902 | 8 - Variable/structure/procedure '...' possible not used |
||
903 | 9 - Non-initialized variable may have been used |
||
904 | 10 - Return flag was destroyed |
||
905 | 11 - Code may not be executable |
||
906 | 12 - Don't use local/parametric values in inline procedures |
||
7544 | leency | 907 | Return to contents. |
7496 | leency | 908 | |
909 | |||
7544 | leency | 910 | |
7496 | leency | 911 | 2.1.18 /WSI - короткая таблица импорта. |
7544 | leency | 912 | |
7496 | leency | 913 | |
914 | Таблица импорта обычно состоит в свою очередь из четырех таблиц. Две |
||
915 | таблицы LookUp Table и Import Address Table абсолютно одинаковы. |
||
916 | |||
917 | Опцией командной строки /WSI Вы можете заставить компилятор |
||
918 | генерировать только одну из этих двух одинаковых таблиц (генерируется |
||
919 | только Import Address Table). Тем самым у Вас получится более компактная |
||
920 | таблица импорта, что приведет, в некоторых случаях, к созданию более |
||
921 | компактного выходного файла. |
||
7544 | leency | 922 | Return to contents. |
7496 | leency | 923 | |
924 | |||
7544 | leency | 925 | |
7496 | leency | 926 | 2.2 Директивы транслятора. |
7544 | leency | 927 | |
7496 | leency | 928 | |
929 | C-- не содержит препроцессор. Тем не менее, есть несколько функций |
||
930 | очень похожих на функции C препроцессора. |
||
931 | |||
932 | Они даются как директивы транслятора. Все директивы транслятора |
||
933 | начинаются с вопросительного знака ? либо с символа #. Вот список имеющихся |
||
934 | директив и их назначение: |
||
935 | |||
936 | ? align [val] Выровнять данные программы на четный по |
||
937 | умолчанию или на адрес кратный величине val. |
||
938 | |||
939 | ? aligncode [val] Выровнять код программы на четный по |
||
940 | умолчанию или на адрес кратный величине val. |
||
7544 | leency | 941 | Заполнение производится кодом 0x90. |
7496 | leency | 942 | |
943 | ? aligner (aligner value) определить значение байта вставки. |
||
944 | |||
945 | ? alignword (TRUE or FALSE) разрешает или запрещает выравнивание на |
||
946 | четный адрес переменных типа word и int, |
||
947 | значение по умолчанию TRUE. |
||
948 | |||
949 | ? argc (TRUE or FALSE) Включить или отключить альтернативный |
||
950 | обработчик командной строки. |
||
951 | |||
952 | ? atexit Вставляет в startup код поддержки процедуры |
||
953 | ATEXIT(). |
||
954 | |||
955 | ? code32 (TRUE/FALSE) разрешает/запрещает генерацию 32-битного |
||
956 | кода. |
||
957 | |||
958 | ? codesize оптимизация размера кода (в ущерб скорости). |
||
959 | |||
960 | ? compilerversion min-vers указывает, компилятор какой версии необходим |
||
961 | для компиляции данной программы. |
||
962 | |||
963 | ? ctrl_c (TRUE or FALSE ) разрешает или запрещает игнорирование |
||
964 | нажатия CTRL-C. |
||
965 | |||
966 | ? dataseg (value) указывает компилятору сегментный адрес ОЗУ |
||
967 | для переменных при компиляции ROM-BIOS. |
||
968 | |||
969 | ? define (identifier) (token) определяет идентификатор. |
||
970 | |||
971 | ? DOSrequired (номер) устанавливает минимальную требуемую версию |
||
972 | DOS: старший байт - номер версии, |
||
973 | младший байт - номер модификации: |
||
974 | 0x0101 для версии 1.1 DOS |
||
975 | 0x0315 для версии 3.21 DOS |
||
976 | 0x0303 для версии 3.3 DOS |
||
977 | 0x0600 для версии 6.0 DOS |
||
978 | 0x0602 для версии 6.2 DOS и т.д. |
||
979 | |||
980 | ? dosstring (TRUE/FALSE) указывает компилятору, что в качестве |
||
981 | терминатора строки надо использовать символ $ |
||
982 | |||
983 | ? else генерирует альтернативный код если ?ifdef или |
||
984 | ?ifndef принимают значение FALSE (пример |
||
985 | использования смотрите в файле FPU.H--) |
||
986 | |||
987 | ? endif указывает на конец действия директив ifdef и |
||
988 | ifndef |
||
989 | |||
990 | ? fastcallapi (FALSE/TRUE) запретить/разрешить генерацию быстрого вызова |
||
991 | API-процедур (по умолчанию разрешено). |
||
992 | Директива работает при компиляции программ |
||
993 | под Windows. |
||
994 | |||
995 | ? fixuptable (TRUE/FALSE) разрешить/запретить создание FixUp таблицы |
||
996 | (по умолчанию запрещено). Директива работает |
||
997 | при компиляции программ под Windows. |
||
998 | |||
999 | ? ifdef (identifier) если идентификатор определен, то возвращает |
||
1000 | TRUE иначе FALSE |
||
1001 | |||
1002 | ? imagebase value задает адрес Image Base. По умолчанию этот |
||
1003 | адрес равен 0x400000. Директива работает при |
||
1004 | компиляции программ под Windows. |
||
1005 | |||
1006 | ? ifndef (identifier) если идентификатор определен, то возвращает |
||
1007 | FALSE иначе TRUE |
||
1008 | |||
1009 | ? include ("filename") включает другой файл. |
||
1010 | |||
1011 | ? includepath ("path") указание компилятору, в какой директории надо |
||
1012 | искать включаемые файлы |
||
1013 | |||
1014 | ? initallvar инициализирует 0 все неинициализированные |
||
1015 | переменные. |
||
1016 | |||
1017 | ? jumptomain (NONE, SHORT, NEAR or FALSE) |
||
1018 | устанавливает тип перехода к main(), |
||
1019 | значение по умолчанию - NEAR. |
||
1020 | |||
1021 | ? maxerrors (number) максимальное количество найденных ошибок, |
||
1022 | превысив которое транслятор прекращает |
||
1023 | работу, значение по умолчанию - 16. |
||
1024 | |||
1025 | ? movedatarom (TRUE/FALSE) указывает компилятору о необходимости |
||
1026 | переноса данных из ПЗУ в ОЗУ. |
||
1027 | |||
1028 | ? parsecommandline (TRUE or FALSE) |
||
1029 | включает в программу блок кода для |
||
1030 | синтаксического анализа командной строки |
||
1031 | значение по умолчанию FALSE. |
||
1032 | |||
1033 | ? pragma может объявлять несколько других директив |
||
1034 | |||
1035 | ? print (number or string) выводит на экран строку или число. |
||
1036 | |||
1037 | ? printhex (number) выводит на экран число в шестнадцатеричном |
||
1038 | коде. |
||
1039 | |||
1040 | ? randombyte вставляет в код программы байт случайного |
||
1041 | значения. |
||
1042 | |||
1043 | ? resize (TRUE or FALSE) включает функцию изменения после запуска |
||
1044 | размера выделенного программе блока памяти |
||
1045 | на минимально требуемый объем, |
||
1046 | значение по умолчанию TRUE. |
||
1047 | |||
1048 | ? resizemessage (string) сообщение, выводимое на экран перед |
||
1049 | аварийным прерыванием выполнения программы, |
||
1050 | если изменение размера выделенного программе |
||
1051 | блока памяти не выполнено. |
||
1052 | |||
1053 | ? setdinproc по этой директиве компилятор немедленно |
||
1054 | вставляет в код компилируемой программы все |
||
1055 | вызывавшиеся ранее динамические процедуры. |
||
1056 | |||
1057 | ? sizerom (value) указывает компилятору размер ПЗУ. |
||
1058 | |||
1059 | ? speed оптимизация быстродействия (значение |
||
1060 | по умолчанию) в ущерб размеру кода. |
||
1061 | |||
1062 | ? stack (number) определяет размер стека программы в байтах. |
||
1063 | |||
1064 | ? startaddress (number) устанавливает стартовый адрес начала кода, |
||
1065 | значение по умолчанию 0x100. |
||
1066 | |||
1067 | ? startuptomain в com-файлах размещает startup-код в |
||
1068 | процедуре main(). |
||
1069 | |||
1070 | ? startusevar (number) указывает адрес, с которого разрешено |
||
1071 | использовать ячейки памяти под |
||
1072 | неинициализированные переменные. |
||
1073 | |||
1074 | ? sysattribute (значение) эта директива передает компилятору атрибут |
||
1075 | создаваемого драйвера. По умолчанию |
||
1076 | устанавливается значение 0x2000. |
||
1077 | Действует только с ключом /SYS. |
||
1078 | |||
1079 | ? sysname <текстовая строка> эта директива передает компилятору имя |
||
1080 | будущего драйвера. По умолчанию |
||
1081 | присваивается имя NO_NAME. Длина имени не |
||
1082 | более 8 символов. Действует только с ключом |
||
1083 | /SYS. |
||
1084 | |||
1085 | ? syscommand |
||
1086 | является обязательной при создании |
||
1087 | драйверов. По этой директиве компилятору |
||
1088 | передается список имен процедур обработки |
||
1089 | команд драйвера. Действует только с ключом |
||
1090 | /SYS. |
||
1091 | |||
1092 | ? warning (TRUE or FALSE) эта директива разрешает или запрещает выдачу |
||
1093 | предупреждений. Директива действует только в |
||
1094 | пределах текущего файла и не влияет на |
||
1095 | включаемые файлы. |
||
1096 | |||
1097 | ? winmonoblock FALSE запрещает размещение таблиц файла формата PE |
||
1098 | в одну секцию. |
||
1099 | |||
1100 | ? undef уничтожает константы объявленные директивой |
||
1101 | ? define |
||
1102 | |||
1103 | ? use8086 ограничивается при генерации объектного кода |
||
1104 | командами 8088/8086 (значение по умолчанию). |
||
1105 | |||
1106 | ? use8088 ограничивается при генерации объектного кода |
||
1107 | командами 8088/8086 (значение по умолчанию). |
||
1108 | |||
1109 | ? use80186 допускает при генерации объектного кода |
||
1110 | команды и оптимизацию для процессора 80186. |
||
1111 | |||
1112 | ? use80286 допускает при генерации объектного кода |
||
1113 | команды и оптимизацию для процессора 80286. |
||
1114 | |||
1115 | ? use80386 допускает при генерации объектного кода |
||
1116 | команды и оптимизацию для процессора 80386. |
||
1117 | |||
1118 | ? use80486 допускает при генерации объектного кода |
||
1119 | команды и оптимизацию для процессора 80486. |
||
1120 | |||
1121 | ? usePentium допускает при генерации объектного кода |
||
1122 | команды и оптимизацию для процессора Pentium. |
||
1123 | |||
1124 | ? useMMX допускает при генерации объектного кода |
||
1125 | команды и оптимизацию для процессора Pentium |
||
1126 | MMX. |
||
1127 | |||
1128 | ? usestartup разрешает компилятору использовать ячейки |
||
1129 | памяти, занимаемые кодом начальной |
||
1130 | инициализации программы. |
||
7544 | leency | 1131 | Return to contents. |
7496 | leency | 1132 | |
1133 | |||
7544 | leency | 1134 | |
7496 | leency | 1135 | 2.2.1 ?ifdef/?ifndef |
7544 | leency | 1136 | |
7496 | leency | 1137 | |
1138 | Ранее директива ?ifdef срабатывала на наличие константы независимо |
||
1139 | от значения ее величины, а директива ?ifndef срабатывала на отсутствие |
||
1140 | константы в компилируемом файле. Теперь ?indef срабатывает лишь на |
||
1141 | константу отличную от FALSE, а ?ifndef срабатывает как на отсутствие |
||
1142 | константы в компилируемом файле, так и на константу имеющую значение |
||
1143 | FALSE. |
||
1144 | |||
1145 | Для директив ?ifdef/?ifndef зарезервированы константы codesize и |
||
1146 | speed, которые принимают значение TRUE или FALSE в зависимости от режима |
||
1147 | оптимизации. Это будет полезным для создания более гибких библиотек. |
||
1148 | |||
1149 | Есть возможность проверки типа CPU для которого ведется компиляция. |
||
1150 | Допустимые варианты синтаксиса: |
||
1151 | |||
7544 | leency | 1152 | ?ifdef cpu > 1 //если программа компилируется для CPU выше 80186 |
7496 | leency | 1153 | ?ifndef cpu >= 2 // -------//------------- не больше или равно 80286 |
1154 | ?ifdef cpu == 3 // -------//------------- равно 80386 |
||
1155 | ?ifdef cpu != 0 // -------//------------- не равен 8086 |
||
1156 | ?ifdef cpu < 3 // -------//------------- хуже 80386 |
||
1157 | ?ifdef cpu <= 2 // -------//------------- хуже или равен 80286 |
||
1158 | |||
1159 | Эта директива позволит Вам писать одну процедуру для различных типов |
||
1160 | CPU. |
||
7544 | leency | 1161 | Return to contents. |
7496 | leency | 1162 | |
1163 | |||
7544 | leency | 1164 | |
7496 | leency | 1165 | 2.2.2 ?initallvar |
7544 | leency | 1166 | |
7496 | leency | 1167 | |
1168 | Директивой ?initallvar TRUE включается режим при котором всем |
||
1169 | неинициализированным переменным будет присвоено нулевое значение и они |
||
1170 | будут располагаться в том месте, где были объявлены. Т.е. практически |
||
1171 | исчезнут неинициализированные переменные. Это может быть полезным при |
||
1172 | написании драйверов и резидентных программ. |
||
1173 | |||
1174 | Параметр FALSE этой директивы отключает этот режим. |
||
1175 | По умолчанию эта директива установлена в FALSE. |
||
7544 | leency | 1176 | Return to contents. |
7496 | leency | 1177 | |
1178 | |||
7544 | leency | 1179 | |
7496 | leency | 1180 | 2.2.3 ?usestartup |
7544 | leency | 1181 | |
7496 | leency | 1182 | |
1183 | Директива ?usestartup разрешает компилятору использовать ячейки кода |
||
1184 | начальной инициализации программы (startup) для последующего размещения в |
||
1185 | них неинициализированных переменных. Это может быть полезным для получения |
||
1186 | более компактного кода, как обычных программ, так и в особенности |
||
1187 | резидентных. |
||
1188 | |||
1189 | Эту директиву применяют только для генерации *.COM файлов. |
||
7544 | leency | 1190 | Return to contents. |
7496 | leency | 1191 | |
1192 | |||
7544 | leency | 1193 | |
7496 | leency | 1194 | 2.2.4 ?startusevar |
7544 | leency | 1195 | |
7496 | leency | 1196 | |
1197 | Директивой ?startusevar можно указать начальный адрес с которого |
||
1198 | компилятор будет распределять память для неинициализированных переменных. |
||
1199 | Например, получив директиву ?startusevar 0x53 компилятор будет |
||
1200 | располагать неинициализированные переменные, начиная с адреса 0x53. Это |
||
1201 | может быть полезным для получения более компактного кода как для |
||
1202 | резидентных, так и для обычных программ. |
||
1203 | |||
1204 | Эту директиву применяют только для генерации *.COM файлов. |
||
7544 | leency | 1205 | Return to contents. |
7496 | leency | 1206 | |
1207 | |||
7544 | leency | 1208 | |
7496 | leency | 1209 | 2.2.5 ?atexit |
7544 | leency | 1210 | |
7496 | leency | 1211 | |
1212 | Директива ?atexit добавляет в startup программы код поддержки |
||
1213 | процедуры ATEXIT, резервирует место для хранения 16 адресов процедур и |
||
1214 | изменяет код процедур ABORT и EXIT. |
||
1215 | |||
1216 | Процедура ATEXIT регистрирует процедуру, адрес которой передается ей в |
||
1217 | качестве параметра, как процедуру завершения программы. Эта процедура |
||
1218 | будет вызвана в момент завершения программы процедурами ABORT или EXIT |
||
1219 | или инструкцией RET из main. |
||
1220 | |||
1221 | Всего можно зарегистрировать до 16 процедур. Процедуры вызываются в |
||
1222 | порядке обратном порядку их регистрации. |
||
7544 | leency | 1223 | Return to contents. |
7496 | leency | 1224 | |
1225 | |||
7544 | leency | 1226 | |
7496 | leency | 1227 | 2.2.6 ?startuptomain |
7544 | leency | 1228 | |
7496 | leency | 1229 | |
1230 | По этой директиве компилятор в начале файла делает jmp на начало |
||
1231 | процедуры main(). Перед началом компиляции этой процедуры компилятор |
||
1232 | начнет компиляцию startup кода и лишь затем будет продолжена компиляция |
||
1233 | процедуры main(). Тем самым startup код окажется не в начале файла, как |
||
1234 | это происходит обычно, а в теле процедуры main(). Это будет полезным при |
||
1235 | компиляции резидентных программ (TSR). |
||
1236 | |||
1237 | Директива ?startuptomain работает только при компиляции com-файлов. |
||
7544 | leency | 1238 | Return to contents. |
7496 | leency | 1239 | |
1240 | |||
7544 | leency | 1241 | |
7496 | leency | 1242 | 2.2.7 ?undef |
7544 | leency | 1243 | |
7496 | leency | 1244 | |
1245 | Эта директива уничтожает константы объявленные директивой ?define. Ее |
||
1246 | можно применять для изменения в процессе компиляции значения какой-нибудь |
||
1247 | константы. |
||
7544 | leency | 1248 | Return to contents. |
7496 | leency | 1249 | |
1250 | |||
7544 | leency | 1251 | |
7496 | leency | 1252 | 2.2.8 ?align и ?aligncode |
7544 | leency | 1253 | |
7496 | leency | 1254 | |
1255 | В C-- существует директива ?align, которая делает однократное |
||
1256 | выравнивание данных на четный адрес. Но если к этой директиве добавить |
||
1257 | число, то выравнивание будет произведено на адрес кратный этому числу. |
||
1258 | Например директива ?align 4 дополнит сегмент данных до адреса кратного |
||
1259 | 4. При выравнивании будут вставляться байты, значения которых определяются |
||
1260 | директивой ?aligner, по умолчанию это значение равно нулю. Директива |
||
1261 | ?align производит выравнивание только в сегменте данных. В тех моделях |
||
1262 | памяти, в которых сегмент данных и кода совпадают эту директиву можно |
||
1263 | применять и для выравнивания начала процедур. |
||
1264 | |||
1265 | Директива ?aligncode [value] делает выравнивание в сегменте кода на |
||
1266 | адрес кратный значению value, по умолчанию на четный адрес. Значение байта |
||
1267 | заполнения в этой директиве является число 0x90 - код инструкции NOP. |
||
1268 | Значение байта заполнения для этой директивы изменить нельзя. Т.о. эту |
||
1269 | директиву можно применять и внутри исполняемого кода. Например, если Вы |
||
1270 | хотите получить быстрый код на 486 процессоре, то рекомендуется делать |
||
1271 | выравнивание начала процедур и циклов на адрес кратный 16. |
||
7544 | leency | 1272 | Return to contents. |
7496 | leency | 1273 | |
1274 | |||
7544 | leency | 1275 | |
7496 | leency | 1276 | 2.2.9 ?pragma |
7544 | leency | 1277 | |
7496 | leency | 1278 | |
1279 | Директива #pragma это многофункциональнальная директива, которая в |
||
1280 | свою очередь имеет свои директивы: |
||
1281 | |||
1282 | option |
||
1283 | Директива option позволяет включить в Ваш код опции командной строки |
||
1284 | компилятора. Некоторые опции не могут быть использованы в этой директиве; |
||
1285 | другие должны помещаться в самом начале исходного текста. Пример: |
||
1286 | |||
1287 | #pragma option w32c |
||
1288 | |||
1289 | Эта директива объявляет компилятору, что надо создать консольный |
||
1290 | 32-битный файл под windows. |
||
1291 | |||
1292 | startup |
||
1293 | Директивой startup можно указать функцию, которая будет выполнена перед |
||
1294 | запуском процедуры main. Эта директива имеет такой формат: |
||
1295 | |||
1296 | #pragma startup procname |
||
1297 | |||
1298 | Количество раз, которое можно применять эту директиву в одной |
||
1299 | программе не ограничено, но реально можно использовать лишь несколько |
||
1300 | тысяч раз. |
||
1301 | |||
1302 | line |
||
1303 | Директива line выводит на экран номер текущей строки и имя файла. |
||
1304 | Дополнительно может выводиться содержимое строки находящееся после слова |
||
1305 | line. Пример: |
||
1306 | |||
1307 | #pragma line information |
||
1308 | |||
1309 | Встретив эту директиву, компилятор выведет на экран номер строки и имя |
||
1310 | файла. Также будет выведено сообщение справа от слова line, если оно |
||
1311 | есть. |
||
1312 | |||
1313 | resource |
||
1314 | Эта директива может принимать значения start и end. Эти два |
||
1315 | значения выделяют начало и конец блока ресурсов, если вы используете его |
||
1316 | непосредственно в исходном коде файла, а не в отдельном файле. Пример: |
||
1317 | |||
1318 | #pragma resource start |
||
1319 | |||
1320 | MyMenu MENU DISCARDABLE |
||
1321 | BEGIN POPUP "Files",HELP |
||
1322 | BEGIN |
||
1323 | MENUITEM "Open", ID_OPEN |
||
1324 | MENUITEM "Save", ID_SAVE |
||
1325 | MENUITEM SEPARATOR |
||
1326 | MENUITEM "Exit", ID_EXIT |
||
1327 | END |
||
1328 | MENUITEM "Other", 65535 |
||
1329 | END |
||
1330 | |||
1331 | #pragma resource end |
||
7544 | leency | 1332 | Return to contents. |
7496 | leency | 1333 | |
1334 | |||
7544 | leency | 1335 | |
7496 | leency | 1336 | 3. Константы. |
1337 | |||
1338 | 3.1 Числовые константы. |
||
7544 | leency | 1339 | |
7496 | leency | 1340 | |
1341 | Представление числовых констант в виде десятичных чисел (чисел с |
||
1342 | основанием 10) и шестнадцатеричных чисел (основание счисления 16) полностью |
||
1343 | аналогично языку C. |
||
1344 | |||
1345 | При двоичном представлении чисел (основание 2) число должно начинаться |
||
1346 | с символов 0b, за которыми без пробела идет последовательность нулей и |
||
1347 | единиц. |
||
1348 | |||
1349 | При восьмеричном представлении чисел (основание 8) число должно |
||
1350 | начинаться с символов 0o, за которыми без пробела идет последовательность |
||
1351 | цифр. |
||
1352 | |||
1353 | Вещественное число отличается от целого по наличию в нем точки. |
||
1354 | Начинаться вещественное число должно либо цифрой от 0 до 9, либо знаком |
||
1355 | минус. Необязательной частью вещественного числа является показатель |
||
1356 | степени. Показатель степени отделяется от числа символом e или E. |
||
1357 | Пробелы недопустимы. |
||
1358 | |||
1359 | Примеры: |
||
1360 | 0b11111111 // двоичное представление числа 255 |
||
1361 | 0x00F // шестнадцатеричное представление числа 15 |
||
1362 | 0o10 // восьмеричное представление числа 8 |
||
1363 | 1.234567E-20 // вещественное число |
||
1364 | |||
1365 | C-- вместе с традиционным C-стилем шестнадцатеричных чисел понимает и |
||
1366 | числа записанные в стиле ассемблера. Для тех, кто вдруг не знает, сообщаю, |
||
1367 | что шестнадцатеричные числа в ассемблере имеют на конце символ h или H. |
||
1368 | Если первый символ шестнадцатеричного числа больше 9, то перед ним |
||
1369 | обязательно должен быть записан символ 0. Примеры: |
||
1370 | |||
1371 | 1234h |
||
1372 | 0A000H |
||
1373 | |||
1374 | К числовым константам можно писать суффиксы L, U и F. Фактически |
||
1375 | эти суффиксы в C-- не играют никакой роли, компилятор их просто |
||
1376 | проглатывает. Пример: |
||
1377 | |||
1378 | #define DEF 1L |
||
1379 | #define DEF2 2Lu |
||
1380 | #define DEF3 3.0F |
||
1381 | |||
1382 | Эти суффиксы не зависят от регистра, т.е. их можно писать как |
||
1383 | маленькими, так и большими буквами. |
||
7544 | leency | 1384 | Return to contents. |
7496 | leency | 1385 | |
1386 | |||
7544 | leency | 1387 | |
7496 | leency | 1388 | 3.2 Символьные константы. |
7544 | leency | 1389 | |
7496 | leency | 1390 | |
1391 | Одиночные символьные константы, как и в C, должны заключаться в |
||
1392 | одиночные кавычки '. |
||
1393 | |||
1394 | Также как и в C, для обозначения специальных символов служит обратная |
||
1395 | наклонная черта вправо \ с последующим за ней ключевым символом (или |
||
1396 | несколькими символами). Поддерживаются следующие специальные символы: |
||
1397 | |||
1398 | \a /* звуковой сигнал */ |
||
1399 | \b /* забой */ |
||
1400 | \f /* перевод страницы */ |
||
1401 | \l /* перевод строки */ |
||
1402 | \n /* возврат каретки*/ |
||
1403 | \r /* возврат каретки*/ |
||
1404 | \t /* табуляция */ |
||
1405 | \x?? /* символ ASCII, соответствующий байтовому представлению, |
||
1406 | состоящему из двух шестнадцатеричных цифр, расположенных |
||
1407 | на месте знаков вопроса */ |
||
1408 | \??? /* символ ASCII, соответствующий байтовому представлению, |
||
1409 | состоящему из трех десятичных цифр, расположенных |
||
1410 | на месте знаков вопроса */ |
||
1411 | |||
1412 | Любой другой символ после обратной наклонной черты вправо будет принят |
||
1413 | как простой символ. |
||
1414 | |||
1415 | Символ "Одиночная кавычка" ' может быть введен при помощи конструкции |
||
1416 | \' |
||
1417 | |||
1418 | Символ NULL может быть введен как '' |
||
1419 | |||
1420 | В C-- поддерживаются и многобуквенные символьные константы. Примеры |
||
1421 | многобуквенных символьных констант: |
||
1422 | |||
1423 | 'ab' |
||
1424 | 'the' |
||
1425 | 'this is large' |
||
1426 | |||
1427 | Никакого ограничения на число символов в символьной константе не |
||
1428 | накладывается, но различаются только последние 4 символа. Это - максимум, |
||
1429 | который может быть сохранен в 32-разрядной переменной. Например, константы |
||
1430 | this is large и arge - одинаковы. |
||
1431 | |||
1432 | C-- обрабатывает все символьные константы как числовые значения ASCII |
||
1433 | символов. Для многобуквенных символьных констант первый символ |
||
1434 | соответствует старшим разрядам, таким образом, значение для ab будет |
||
1435 | закодировано как a*256+b. |
||
7544 | leency | 1436 | Return to contents. |
7496 | leency | 1437 | |
1438 | |||
7544 | leency | 1439 | |
7496 | leency | 1440 | 3.3 Строковые константы. |
7544 | leency | 1441 | |
7496 | leency | 1442 | |
1443 | Строковые константы, как и в C, заключаются в двойные кавычки ("). |
||
1444 | Специальные символы внутри строк обозначаются так же, как и в символьных |
||
1445 | константах. Все специальные символы имеют то же значение, что и в |
||
1446 | символьных константах за исключением \n, который имеет значение новая |
||
1447 | строка и заменяет собой пару символов возврат каретки и перевод |
||
1448 | строки. |
||
1449 | |||
1450 | В настоящее время наибольшая длина строковой константы - 2048 символов, |
||
1451 | включая символ-ограничитель 0, таким образом, максимум 2047 значащих |
||
1452 | символов. |
||
7544 | leency | 1453 | Return to contents. |
7496 | leency | 1454 | |
1455 | |||
7544 | leency | 1456 | |
7496 | leency | 1457 | 3.4 Постоянные выражения. |
7544 | leency | 1458 | |
7496 | leency | 1459 | |
1460 | Постоянное выражение - одиночная числовая константа или несколько |
||
1461 | числовых констант, связанных между собой операторами. Числовое значение |
||
1462 | выражения вычисляется один раз во время компиляции и далее используется |
||
1463 | только его постоянное значение. |
||
1464 | |||
1465 | Подобно всем выражениям в C--, постоянные выражения всегда вычисляются |
||
1466 | слева направо, невзирая на правила арифметики! Это совершенно отлично от |
||
1467 | других языков, и при написании выражений надо быть осторожным и помнить, |
||
1468 | что 2+3*2=10 а не 8. |
||
1469 | |||
1470 | Некоторые примеры постоянных выражений: |
||
1471 | 45 & 1 + 3 // равняется 4 |
||
1472 | 14 - 1 / 2 // равняется 6 (помните целочисленные значения) |
||
1473 | 1 * 2 * 3 / 2 + 4 // равняется 7 |
||
1474 | Примеры с применением вещественных чисел: |
||
1475 | 3.23*1.53+2.0E2 // равняется 204.9419 |
||
7544 | leency | 1476 | Return to contents. |
7496 | leency | 1477 | |
1478 | |||
7544 | leency | 1479 | |
7496 | leency | 1480 | 4. Выражения. |
1481 | |||
1482 | 4.1 Типы выражений. |
||
7544 | leency | 1483 | |
7496 | leency | 1484 | |
1485 | Имеются три типа выражений в C--, не считая постоянных выражений. Это |
||
1486 | выражения типа EAX/AX/AL, выражения типа неEAX/AX/AL и условные выражения. |
||
1487 | Все C-- выражения вычисляются слева направо, независимо от старшинства |
||
1488 | входящих в выражение математических операций. |
||
7544 | leency | 1489 | Return to contents. |
7496 | leency | 1490 | |
1491 | |||
7544 | leency | 1492 | |
7496 | leency | 1493 | 4.2 Выражения типа EAX/AX/AL. |
7544 | leency | 1494 | |
7496 | leency | 1495 | |
1496 | Этот тип выражений применяется в случае, когда его результат может быть |
||
1497 | сохранен в переменной в памяти или в регистре EAX или AX или AL. |
||
1498 | |||
1499 | Если результат может быть сохранен в переменных типа byte или char, |
||
1500 | используется нотация AL. |
||
1501 | |||
1502 | Если результат может быть сохранен в переменных типа word или int, |
||
1503 | используется нотация AX. |
||
1504 | |||
1505 | Если результат может быть сохранен в переменных типа dword, long или |
||
1506 | float, используется нотация EAX. |
||
7544 | leency | 1507 | Return to contents. |
7496 | leency | 1508 | |
1509 | |||
7544 | leency | 1510 | |
7496 | leency | 1511 | 4.3 Выражения использующие получатель при вычислении выражения. |
7544 | leency | 1512 | |
7496 | leency | 1513 | |
1514 | Если в правой части выражения используется переменная являющаяся |
||
1515 | одновременно и приемником, то такие выражения дают различные результаты в |
||
1516 | зависимости от того является приемник регистром или переменной памяти. Это |
||
1517 | связано с тем, что при вычислении выражения в переменную памяти, вычисление |
||
1518 | производится сначала в регистр EAX/AX/AL, и лишь после окончания вычисления |
||
1519 | результат будет записан в приемник. Если же приемником является регистр, то |
||
1520 | его значение будет меняться после каждой операции вычисления. Пример: |
||
1521 | |||
1522 | int var; |
||
1523 | var = BX = 2; |
||
1524 | var = 3 + var; // результатом будет 5 |
||
1525 | BX = 3 + BX; // результатом будет 6 |
||
7544 | leency | 1526 | Return to contents. |
7496 | leency | 1527 | |
1528 | |||
7544 | leency | 1529 | |
7496 | leency | 1530 | 4.4 Не - EAX/AX/AL выражения. |
7544 | leency | 1531 | |
7496 | leency | 1532 | |
1533 | Этот тип выражений применяется в случае, когда его результат должен |
||
1534 | быть сохранен в любом другом регистре, отличном от аккумулятора EAX, AX |
||
1535 | или AL. В процессе вычисления выражения этого типа меняется только |
||
1536 | содержимое указанного регистра-получателя, все другие регистры будут |
||
1537 | сохранены. Если регистром-получателем служит байтовый регистр, а при |
||
1538 | вычислении используются величины размером в слово, одновременно с записью в |
||
1539 | младший байт может быть разрушено содержимое старшего байта |
||
1540 | регистра-получателя. |
||
1541 | |||
1542 | Это обстоятельство накладывает некоторые ограничения на операции и |
||
1543 | операнды, допустимые в выражениях типа не EAX/AX/AL. Внутри выражений |
||
1544 | байтового типа не допускается: |
||
1545 | |||
1546 | - делать вызовы МАКРОКОМАНД, |
||
1547 | - делать вызовы РЕГИСТРОВЫХ процедур |
||
1548 | - делать вызовы СТЕКОВЫХ процедур |
||
1549 | |||
1550 | Ранее в не-EAX/AX/AL выражениях было можно использовать лишь |
||
1551 | операции: сложения, вычитания, XOR, OR, AND. Теперь для 16 и 32 битных |
||
1552 | регистров почти все ограничения сняты. Но есть еще ограничения на регистры. |
||
1553 | Например, если в выражении используется сдвиг на значение переменной, а |
||
1554 | приемником являются регистры CX/ECX, то такое выражение компилятор не будет |
||
1555 | компилировать: |
||
1556 | |||
1557 | CX = var * SI * 3 * var >> 3; //вызовет сообщение об ошибке |
||
1558 | |||
1559 | Примечание: для 8 битных не-AL выражений умножать можно только на |
||
1560 | числа: 0, 1, 2, 4, 8, 16, 32, 64 и 128. Все эти ограничения связаны со |
||
1561 | стремлением не разрушать другие регистры при использовании не-EAX/AX/AL |
||
1562 | выражений. |
||
7544 | leency | 1563 | Return to contents. |
7496 | leency | 1564 | |
1565 | |||
7544 | leency | 1566 | |
7496 | leency | 1567 | 4.5 Условные выражения. |
7544 | leency | 1568 | |
7496 | leency | 1569 | |
1570 | Условные выражения - выражения, результатом вычисления которых является |
||
1571 | логическое значение да или нет, используемое в операторе if и циклах do {} |
||
1572 | while, while, for. |
||
1573 | |||
1574 | Имеются два типа условных выражений, простые и сложные. |
||
1575 | |||
1576 | Возможно логическое объединение условий. |
||
7544 | leency | 1577 | Return to contents. |
7496 | leency | 1578 | |
1579 | |||
7544 | leency | 1580 | |
7496 | leency | 1581 | 4.5.1 Простые условные выражения. |
7544 | leency | 1582 | |
7496 | leency | 1583 | |
1584 | Простые условные выражения - одиночная лексема или выражение, которое |
||
1585 | примет значение да, если расчетное значение отлично от нуля, или значение |
||
1586 | нет, если расчетное значение равно нулю. |
||
7544 | leency | 1587 | Return to contents. |
7496 | leency | 1588 | |
1589 | |||
7544 | leency | 1590 | |
7496 | leency | 1591 | 4.5.2 Сложные условные выражения. |
7544 | leency | 1592 | |
7496 | leency | 1593 | |
1594 | Сложные условные выражения имеют следующую форму: |
||
1595 | |||
1596 | (левая_часть оператор_отношения правая_часть) |
||
1597 | |||
1598 | Где: |
||
1599 | левая_часть - любое выражение типа AL/AX/EAX или постоянное выражение. |
||
1600 | Тип выражения определяется по типу первой лексемы |
||
1601 | (регистра или переменной); значение типа по умолчанию - |
||
1602 | word для 16-битных программ и dword для 32-битных. Если |
||
1603 | желателен другой тип, перед выражением ставится |
||
1604 | соответствующее ключевое слово, определяющее его тип: |
||
1605 | byte, char, int, long, dword или float |
||
1606 | |||
1607 | оператор_отношения - любой из операторов отношения: |
||
1608 | ==, !=, <>, <, >, <=, или >=. |
||
1609 | |||
1610 | правая_часть - любой одиночный регистр, одиночная переменная или |
||
1611 | постоянное выражение. |
||
1612 | |||
1613 | Примеры правильных сложных условных выражений: |
||
1614 | |||
1615 | (X + y > z) |
||
1616 | (int CX*DX < = 12*3) |
||
1617 | (byte first*second+hold == cnumber) |
||
1618 | |||
1619 | Примеры недопустимых сложных условных выражений: |
||
1620 | |||
1621 | (x+y >= x-y) // правая часть не является одиночной лексемой или |
||
1622 | постоянным выражением. |
||
1623 | (Z = y) // вместо == ошибочно поставлен = |
||
7544 | leency | 1624 | Return to contents. |
7496 | leency | 1625 | |
1626 | |||
7544 | leency | 1627 | |
7496 | leency | 1628 | 4.6 Изменение типа выражения при присваивании. |
7544 | leency | 1629 | |
7496 | leency | 1630 | |
1631 | Если после знака равенства написать тип отличный от типа вычисляемой |
||
1632 | переменной, то все переменные участвующие в процессе вычисления, будут |
||
1633 | преобразовываться к этому новому типу, и лишь конечный результат будет |
||
1634 | преобразован к типу вычисляемой переменной. Пример: |
||
1635 | |||
1636 | int i, a; |
||
1637 | long b; |
||
1638 | char c; |
||
1639 | |||
1640 | i = a * b + c ; |
||
1641 | |||
1642 | Значения переменных a, b, и c в этом примере перед вычислением будут |
||
1643 | преобразованы к типу int (типу переменной i). Но если записать это |
||
1644 | выражение вот так: |
||
1645 | |||
1646 | i = long a * b + c ; |
||
1647 | |||
1648 | то переменные a, b, и c в этом примере перед вычислением будут |
||
1649 | преобразованы к типу long, а конечный результат будет преобразован к типу |
||
1650 | переменной i - int. |
||
7544 | leency | 1651 | Return to contents. |
7496 | leency | 1652 | |
1653 | |||
7544 | leency | 1654 | |
7496 | leency | 1655 | 4.7 Вычисление в регистры EAX/AX/AL со знаком. |
7544 | leency | 1656 | |
7496 | leency | 1657 | |
1658 | По умолчанию все вычисления в регистры производятся как с без знаковыми |
||
1659 | величинами. |
||
1660 | |||
1661 | Например: |
||
1662 | |||
1663 | int a,b,c; |
||
1664 | AX = a * b / c ; |
||
1665 | |||
1666 | При этом компилятор генерировал без знаковые инструкции div и mul, так как |
||
1667 | регистры считаются без знаковыми переменными. Если написать вот так: |
||
1668 | |||
1669 | AX = int a * b / c ; |
||
1670 | |||
1671 | то компилятор сгенерирует инструкции idiv и imul. |
||
1672 | |||
1673 | Обращаю ваше внимание, что для регистра AL можно использовать только |
||
1674 | модификатор char, для AX соответственно только int, а для EAX - long. Для |
||
1675 | остальных регистров подобное делать нельзя. |
||
7544 | leency | 1676 | Return to contents. |
7496 | leency | 1677 | |
1678 | |||
7544 | leency | 1679 | |
7496 | leency | 1680 | 5. Идентификаторы. |
1681 | |||
1682 | 5.1 Формат идентификатора. |
||
7544 | leency | 1683 | |
7496 | leency | 1684 | |
1685 | Идентификаторы в C-- должны начинаться или с символа подчеркивания _ |
||
1686 | или заглавных или строчных букв. Следующие символы могут быть любой |
||
1687 | комбинацией символов подчеркивания, заглавных или строчных букв или чисел |
||
1688 | (от 0 до 9). Общая длина идентификатора не может превышать 64 символа. |
||
1689 | Символы с кодом больше 0x7A (код символа z) недопустимы. |
||
1690 | |||
1691 | Примеры допустимых идентификаторов: |
||
1692 | |||
1693 | _DOG |
||
1694 | Loony12 |
||
1695 | HowdYBoys_AND_Girls |
||
1696 | WOW___ |
||
1697 | X |
||
1698 | |||
1699 | Примеры недопустимых идентификаторов: |
||
1700 | |||
1701 | 12bogus /* не может начинаться с числа */ |
||
1702 | WowisthisalongidentifieryupitsureisnotOyoulengthismorethat64chars |
||
1703 | /*длина идентификатора превышает 64 */ |
||
1704 | Y_es sir /* пробелы недопустимы */ |
||
1705 | The-end /* дефисы недопустимы */ |
||
7544 | leency | 1706 | Return to contents. |
7496 | leency | 1707 | |
1708 | |||
7544 | leency | 1709 | |
7496 | leency | 1710 | 5.2 Зарезервированные идентификаторы. |
7544 | leency | 1711 | |
7496 | leency | 1712 | |
1713 | Список зарезервированных в C-- идентификаторов, которые не могут |
||
1714 | использоваться как общие идентификаторы, поскольку они уже были определены |
||
1715 | или зарезервированы для других целей: |
||
1716 | |||
1717 | BREAK CASE CONTINUE ELSE EXTRACT FALSE FOR |
||
1718 | FROM GOTO IF LOOPNZ RETURN SWITCH TRUE |
||
1719 | WHILE |
||
1720 | |||
1721 | CARRYFLAG MINUSFLAG NOTCARRYFLAG NOTOVERFLOW |
||
1722 | NOTZEROFLAG OVERFLOW PLUSFLAG ZEROFLAG |
||
1723 | |||
1724 | __CODEPTR__ __COMPILER__ __DATAPTR__ __DATESTR__ __DATE__ __DAY__ |
||
1725 | __HOUR__ __LINE__ __MINUTE__ __MONTH__ __POSTPTR__ __SECOND__ |
||
1726 | __TIME__ __VER1__ __VER2__ __WEEKDAY__ __YEAR__ |
||
1727 | |||
1728 | _export asm break byte case cdecl char continue |
||
1729 | default do dword else enum extern far fastcall |
||
1730 | float for goto if inline int interrupt long |
||
1731 | loop loopnz pascal return short signed sizeof static |
||
1732 | stdcall struct switch union unsigned void while word |
||
1733 | |||
1734 | ESCHAR ESBYTE ESINT ESWORD ESLONG ESDWORD ESFLOAT |
||
1735 | CSCHAR CSBYTE CSINT CSWORD CSLONG CSDWORD CSFLOAT |
||
1736 | SSCHAR SSBYTE SSINT SSWORD SSLONG SSDWORD SSFLOAT |
||
1737 | DSCHAR DSBYTE DSINT DSWORD DSLONG DSDWORD DSFLOAT |
||
1738 | FSCHAR FSBYTE FSINT FSWORD FSLONG FSDWORD FSFLOAT |
||
1739 | GSCHAR GSBYTE GSINT GSWORD GSLONG GSDWORD GSFLOAT |
||
1740 | |||
1741 | AX CX DX BX SP BP SI DI |
||
1742 | EAX ECX EDX EBX ESP EBP ESI EDI |
||
1743 | AL CL DL BL AH CH DH BH |
||
1744 | ES CS SS DS FS GS |
||
1745 | |||
1746 | ST(0) ST(1) ST(2) ST(3) ST(4) ST(5) ST(6) ST(7) ST |
||
1747 | st(0) st(1) st(2) st(3) st(4) st(5) st(6) st(7) st |
||
1748 | |||
1749 | Этот список может быть получен из C-- транслятора в любое время, |
||
1750 | запуском его с опцией /WORDS из командной строки. |
||
1751 | |||
1752 | Если Вы пользуетесь при компиляции опцией командной строки /ia, которая |
||
1753 | позволяет использовать ассемблерные инструкции не заключая их в блоки asm и |
||
1754 | без префикса $, то все имена ассемблерных инструкций становятся |
||
1755 | зарезервированными словами. Причем имена ассемблерных инструкций компилятор |
||
1756 | различает независимо от того, написаны они маленькими или большими буквами. |
||
1757 | |||
1758 | Список имен поддерживаемых компилятором ассемблерных инструкции можно |
||
1759 | получить запустив компилятор с опцией /LAI. |
||
1760 | |||
1761 | Кроме этого в ассемблерных инструкциях становятся зарезервированными |
||
1762 | следующие идентификаторы: |
||
1763 | |||
1764 | ax cx dx bx sp bp si di |
||
1765 | eax ecx edx ebx esp ebp esi edi |
||
1766 | al cl dl bl ah ch dh bh |
||
1767 | es cs ss ds fs gs |
||
1768 | |||
1769 | DR0 DR1 DR2 DR3 DR4 DR5 DR6 DR7 |
||
1770 | CR0 CR1 CR2 CR3 CR4 CR5 CR6 CR7 |
||
1771 | TR0 TR1 TR2 TR3 TR4 TR5 TR6 TR7 |
||
1772 | MM0 MM1 MM2 MM3 MM4 MM5 MM6 MM7 |
||
1773 | XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7 |
||
1774 | |||
1775 | dr0 dr1 dr2 dr3 dr4 dr5 dr6 dr7 |
||
1776 | cr0 cr1 cr2 cr3 cr4 cr5 cr6 cr7 |
||
1777 | tr0 tr1 tr2 tr3 tr4 tr5 tr6 tr7 |
||
1778 | mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 |
||
1779 | xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 |
||
7544 | leency | 1780 | Return to contents. |
7496 | leency | 1781 | |
1782 | |||
7544 | leency | 1783 | |
7496 | leency | 1784 | 5.3 Универсальные регистры для 16 и 32-битного режима. |
7544 | leency | 1785 | |
7496 | leency | 1786 | |
1787 | При создании библиотечных процедур очень часто приходится писать |
||
1788 | варианты процедуры для работы в 16-битном и 32-битном режимах, которые |
||
1789 | отличаются друг от друга лишь использованием в них либо 16-битных либо |
||
1790 | 32-битных регистров соответственно. Но можно писать лишь одну процедуру, |
||
1791 | используя в ней новый синтаксис регистров. Если компилятор встретит вот |
||
1792 | такой синтаксис: |
||
1793 | |||
1794 | (E)AX=0; |
||
1795 | |||
1796 | то компилятор будет использовать при компиляции 16-битного кода регистр |
||
1797 | AX, а при компиляции 32-битного кода регистр EAX. |
||
1798 | |||
1799 | Использование автоматических регистров позволит упростить библиотечные |
||
1800 | файлы и сделать их более понятными. |
||
7544 | leency | 1801 | Return to contents. |
7496 | leency | 1802 | |
1803 | |||
7544 | leency | 1804 | |
7496 | leency | 1805 | 5.4 Предопределенные идентификаторы. |
7544 | leency | 1806 | |
7496 | leency | 1807 | |
1808 | Идентификаторы, определяемые компилятором в зависимости от режима |
||
1809 | компиляции: |
||
1810 | |||
1811 | __TLS__ идет компиляция под windows (w32, w32c, dll). |
||
1812 | __DLL__ идет компиляция dll. |
||
1813 | __CONSOLE__ идет компиляция консольного приложения windows |
||
1814 | __WIN32__ идет компиляция GUI-шного приложения |
||
1815 | __FLAT__ компилируется 32-битный код. |
||
1816 | __MSDOS__ компилируется 16-битный код. |
||
1817 | __TINY__ используется модель памяти tiny в 16-битном режиме |
||
1818 | __SMALL__ используется модель памяти small в 16-битном режиме |
||
1819 | __DOS32__ компилируется 32-битный код под DOS (d32) |
||
1820 | __COM__ компилируется com-файл |
||
1821 | __SYS__ компилируется sys-файл |
||
1822 | __ROM__ компилируется rom-файл |
||
1823 | __OBJ__ компилируется obj-файл |
||
1824 | __TEXE__ компилируется exe-файл модели tiny |
||
1825 | __EXE__ компилируется exe-файл модели small |
||
1826 | __MEOS__ компилируется исполняемый файл для MenuetOS |
||
1827 | codesize компиляция ведется с оптимизацией на размер кода |
||
1828 | speed компиляция ведется с оптимизацией на быстродействие кода |
||
1829 | cpu определяет тип процессора для которого ведется компиляция: |
||
7544 | leency | 1830 | |
1831 | 1 - 80186 |
||
1832 | 2 - 80286 |
||
1833 | 3 - 80386 |
||
1834 | 4 - 80486 |
||
1835 | 5 - Pentium |
||
1836 | 6 - Pentium MMX |
||
1837 | 7 - Pentium II |
||
7496 | leency | 1838 | |
1839 | Эти идентификаторы могут быть проверены директивами #ifdef или #ifndef. |
||
1840 | Идентификатор cpu может быть использован лишь с операторами проверки |
||
1841 | условий: |
||
1842 | |||
1843 | #ifdef cpu > 3 //если тип процессора больше 80386 |
||
7544 | leency | 1844 | Return to contents. |
7496 | leency | 1845 | |
1846 | |||
7544 | leency | 1847 | |
7496 | leency | 1848 | 6. Переменные. |
1849 | |||
1850 | 6.1 Типы переменных. |
||
7544 | leency | 1851 | |
7496 | leency | 1852 | |
1853 | В C-- имеется семь типов переменных (именованных областей памяти), это: |
||
1854 | byte, word, dword, char, int, long, float. |
||
1855 | |||
1856 | Следующая таблица показывает размер и диапазон представляемых величин |
||
1857 | каждого из типов переменной: |
||
1858 | |||
1859 | NAME | SIZE | VALUE RANGE | VALUE RANGE |
||
1860 | тип |размер | диапазон представления | диапазон представления |
||
1861 | |в байт.| в десятичной системе | в шестнадцатеричной системе |
||
1862 | --------------------------------------------------------------------------- |
||
1863 | byte | 1 | 0 to 255 | 0x00 to 0xFF |
||
1864 | word | 2 | 0 to 65535 | 0x0000 to 0xFFFF |
||
1865 | dword | 4 | 0 to 4294967295 | 0x00000000 to 0xFFFFFFFF |
||
1866 | char | 1 | -128 to 127 | 0x80 to 0x7F |
||
1867 | int | 2 | -32768 to 32767 | 0x8000 to 0x7FFF |
||
1868 | long | 4 | -2147483648 to 2147483647 | 0x80000000 to 0x7FFFFFFF |
||
1869 | float | 4 | -3,37E38 to +3,37E38 | 0xFF7FFFFF to 0x7FFFFFFF |
||
1870 | |||
1871 | Примечание: для работы с типами float, dword и long используются |
||
1872 | 32-разрядные целочисленные команды, следовательно, для их выполнения нужно |
||
1873 | иметь процессор не хуже 80386, что сейчас не является большой проблемой. |
||
1874 | |||
1875 | Для совместимости со стандартом, принятом в языке C, введены |
||
1876 | новые зарезервированные слова: short, signed, unsigned. Для типа int |
||
1877 | в 32-битном режиме изменена разрядность. Вот таблица всех вариантов новых |
||
1878 | типов данных: |
||
1879 | |||
1880 | --------------------------------------------------------- |
||
1881 | | полный тип |допустимые сокращения|старые аналоги| |
||
1882 | --------------------------------------------------------- |
||
1883 | |signed char |char | char | |
||
1884 | |signed int |signed, int | int/long | |
||
1885 | |signed short int |short, signed short | int | |
||
1886 | |signed long int |long, signed long | long | |
||
1887 | |unsigned char |--- | byte | |
||
1888 | |unsigned int |unsigned | word/dword | |
||
1889 | |unsigned short int|unsigned short | word | |
||
1890 | |unsigned long int |unsigned long | dword | |
||
1891 | --------------------------------------------------------- |
||
1892 | |||
1893 | Старые типы byte, word и dword поддерживаются по прежнему и имеют |
||
1894 | функционально прежнее значение. Изменения коснулись лишь типа int. Он в |
||
1895 | 16-битном режиме, также как и тип unsigned int, имеет 16-битный размер, а |
||
1896 | в 32-битном режиме эти оба типа имеют размер в 32-бита. На первый взгляд |
||
1897 | такие свойства типа int вносят некоторую путаницу, но это дает большой |
||
1898 | выигрыш при использовании этого типа в библиотечных файлах, которые могут |
||
1899 | быть использованы при компиляции 16-битных и 32-битных программ. |
||
7544 | leency | 1900 | Return to contents. |
7496 | leency | 1901 | |
1902 | |||
7544 | leency | 1903 | |
7496 | leency | 1904 | 6.2 Объявление переменных. |
7544 | leency | 1905 | |
7496 | leency | 1906 | |
1907 | Синтаксис для объявления переменных следующий: |
||
1908 | |||
1909 | variable-type identifier; |
||
1910 | |||
1911 | где variable-type - char, byte, int, word, long, dword или float. |
||
1912 | |||
1913 | Одновременно могут быть объявлены несколько идентификаторов одного типа: |
||
1914 | |||
1915 | variable-type identifier1, identifier2, ... , identifierN; |
||
1916 | |||
1917 | Одномерные массивы могут быть объявлены следующим образом: |
||
1918 | |||
1919 | variable-type identifier[elements]; |
||
1920 | |||
1921 | где elements - постоянное выражение для количества переменных этого типа, |
||
1922 | объединенных в массив. |
||
1923 | |||
1924 | Инициализированные массивы можно объявлять без указания числа |
||
1925 | элементов. При этом будет создан массив по фактическому числу элементов. |
||
1926 | |||
1927 | variable-type identifier[] = { const1, const2 }; |
||
1928 | |||
1929 | Переменные при объявлении могут быть проинициализированы следующим |
||
1930 | образом: |
||
1931 | |||
1932 | variable-type identifier = value; |
||
1933 | |||
1934 | Некоторые примеры глобальных объявлений: |
||
1935 | byte i,j; /* объявляет две переменные типа byte с именами i и j */ |
||
1936 | word see[10] /* объявляет массив с именем see, состоящий из 10 |
||
1937 | элементов типа word */ |
||
1938 | int h,x[27] /* объявляет, переменную типа int с именем h, |
||
1939 | и массив с именем x, состоящий из 27 элементов типа int */ |
||
1940 | long size=0; /* объявлена переменная типа long с именем size и ей присвоено |
||
1941 | значение 0. */ |
||
7544 | leency | 1942 | Return to contents. |
7496 | leency | 1943 | |
1944 | |||
7544 | leency | 1945 | |
7496 | leency | 1946 | 6.3 Глобальные переменные. |
7544 | leency | 1947 | |
7496 | leency | 1948 | |
1949 | Глобальные переменные - это переменные, область действия которых |
||
1950 | распространяется на всю программу. В C-- использовать глобальные переменные |
||
1951 | можно в процедурах, расположенных ниже места ее объявления. Т.е. если Вы |
||
1952 | пишите процедуру, в которой используете переменную var, а саму переменную |
||
1953 | объявляете ниже текста процедуры, то компилятор выдаст ошибку. Это связано |
||
1954 | с тем, что компилятор может знать тип переменной только после их |
||
1955 | объявления. Но для таких переменных можно использовать взятие их адреса, |
||
1956 | так как адрес переменной не зависит от его типа. Пример: |
||
1957 | |||
1958 | void Proc(){ |
||
1959 | gvar = 0; /* компилятор выдаст сообщение об ошибке, т.к. он еще не знает |
||
1960 | типа переменной gvar */ |
||
1961 | AX = #gvar; /* несмотря на то, что компилятор не знает и адреса этой |
||
1962 | переменной такое выражение будет откомпилировано */ |
||
1963 | } |
||
1964 | int gvar; |
||
1965 | |||
1966 | Но все же ситуация не безнадежна и нам удастся добиться того, чего мы |
||
1967 | задумали. В этом нам поможет альтернативный синтаксис обращения к |
||
1968 | переменным: |
||
1969 | |||
1970 | void Proc(){ |
||
1971 | DSINT[#gvar] = 0; /* компилятор успешно откомпилирует это выражение т.к. |
||
1972 | ему теперь известен тип переменной gvar */ |
||
1973 | } |
||
1974 | int gvar; |
||
1975 | |||
1976 | Память под глобальные переменные выделяется в сегменте данных. Если |
||
1977 | переменная при объявлении инициализируется (т.е. ей присвоено какое-то |
||
1978 | значение), то переменная будет включена в код компилируемого файла. Если |
||
1979 | переменная не инициализируется, то место для переменной будет |
||
1980 | зарезервировано сразу же за последним байтом скомпилированной программы. |
||
7544 | leency | 1981 | Return to contents. |
7496 | leency | 1982 | |
1983 | |||
7544 | leency | 1984 | |
7496 | leency | 1985 | 6.4 Локальные переменные. |
7544 | leency | 1986 | |
7496 | leency | 1987 | |
1988 | Локальные переменные - это переменные область действия которых |
||
1989 | распространяется лишь в пределах одной процедуры. Объявлять локальные |
||
1990 | переменные, в отличии от современных версий C, можно между именем процедуры |
||
1991 | и первой открывающейся фигурной скобкой. Пример: |
||
1992 | |||
1993 | void PROC () |
||
1994 | int i; //объявлена локальная переменная типа int с именем i |
||
1995 | { |
||
1996 | for ( i=0; i<10; i++ ) WRITE(1); |
||
1997 | } |
||
1998 | |||
1999 | Память под локальные переменные отводится в сегменте стека. |
||
2000 | |||
2001 | К локальным переменным можно отнести и параметры стековых процедур. Под |
||
2002 | них также отводится память в стеке. |
||
2003 | |||
2004 | Можно инициализировать локальные переменные при их объявлении. Но есть |
||
2005 | некоторые ограничения. Нельзя инициализировать массивы и многомерные |
||
2006 | структуры. Инициализировать можно одним значением, т.е нельзя при |
||
2007 | инициализации локальных переменных пользоваться перечислением заключенным в |
||
2008 | фигурные скобки и операторами FROM и EXTRACT. |
||
2009 | |||
2010 | Имена локальных переменных могут совпадать с именами глобальных |
||
2011 | переменных или процедур, но тогда Вы не сможете обратиться к глобальной |
||
2012 | переменной или вызвать одноименную процедуру. |
||
2013 | |||
2014 | Локальные переменные можно объявлять и в начале блока процедуры. Но |
||
2015 | только до начала тела процедуры. Пример: |
||
2016 | |||
2017 | void proc(){ |
||
2018 | int locproc; // объявление локальной процедуры |
||
2019 | locproc=0; // а теперь пошло тело процедуры |
||
2020 | int locproc; // а на это объявление переменной компилятор выдаст сообщение |
||
2021 | // об ошибке, т.к. уже началось тело процедуры |
||
2022 | } |
||
7544 | leency | 2023 | Return to contents. |
7496 | leency | 2024 | |
2025 | |||
7544 | leency | 2026 | |
7496 | leency | 2027 | 6.5 Динамические переменные и структуры. |
7544 | leency | 2028 | |
7496 | leency | 2029 | |
2030 | Наряду с уже известными Вам динамическими процедурами в C-- есть |
||
2031 | возможность использовать динамически и переменные и структуры. Динамические |
||
2032 | переменные и структуры обозначаются также как и динамические процедуры - |
||
2033 | символом двоеточия перед началом их объявления. И также как и динамическая |
||
2034 | процедура, динамическая переменная или структура будет вставлена в код, |
||
2035 | лишь в том случае, если она будет использована в программе. |
||
2036 | |||
2037 | Динамические переменные и структуры найдут применение в библиотеках. |
||
2038 | Использовать их непосредственно в программах нет смысла. |
||
2039 | |||
2040 | У динамических переменных, структур также как и у процедур, есть один |
||
2041 | недостаток - Вы не сможете знать, в каком месте откомпилированного кода они |
||
2042 | будут расположены, и в каком порядке. Но необходимость это знать бывает |
||
2043 | очень редко. |
||
2044 | |||
2045 | Динамические инициализированные переменные и структуры в файле будут |
||
2046 | расположены в его самом конце, после динамических процедур. Эту их |
||
2047 | особенность можно использовать, если Вам будет необходимо, чтобы данные не |
||
2048 | были разбросаны среди кода, а были сгруппированы в одном месте. |
||
7544 | leency | 2049 | Return to contents. |
7496 | leency | 2050 | |
2051 | |||
7544 | leency | 2052 | |
7496 | leency | 2053 | 6.6 Присваивание одного значения нескольким переменным. |
7544 | leency | 2054 | |
7496 | leency | 2055 | |
2056 | Если Вам необходимо присвоить нескольким переменным одинаковые значения: |
||
2057 | |||
2058 | var1=0; |
||
2059 | var2=0; |
||
2060 | var3=0; |
||
2061 | |||
2062 | то теперь это можно записать более коротко: |
||
2063 | |||
2064 | var1=var2=var3=0; |
||
2065 | |||
2066 | При использовании такой записи генерируется более компактный и более |
||
2067 | быстрый код. |
||
7544 | leency | 2068 | Return to contents. |
7496 | leency | 2069 | |
2070 | |||
7544 | leency | 2071 | |
7496 | leency | 2072 | 6.7 Переменные типа float. |
2073 | |||
2074 | 6.7.1 Формат переменных типа float. |
||
7544 | leency | 2075 | |
7496 | leency | 2076 | |
2077 | Для представления значений с плавающей точкой в язык C-- введен тип |
||
2078 | float. Этому типу соответствует действительное число одинарной точности |
||
2079 | FPU. |
||
2080 | |||
2081 | Формат представления данных с плавающей точкой включает три поля: |
||
2082 | знака, мантиссы и порядка. Знак определяется старшим значащим разрядом. |
||
2083 | Поле мантиссы содержит значащие биты числа, а поле порядка содержит |
||
2084 | степень 2 и определяет масштабирующий множитель для мантиссы. |
||
2085 | |||
2086 | 31 30.....23 22........0 |
||
2087 | | | | | | |
||
2088 | | | | -------------- - поле мантиссы |
||
2089 | | ------------------------ - поле порядка |
||
2090 | --------------------------- - бит знака |
||
7544 | leency | 2091 | Return to contents. |
7496 | leency | 2092 | |
2093 | |||
7544 | leency | 2094 | |
7496 | leency | 2095 | 6.7.2 Константы с плавающей точкой. |
7544 | leency | 2096 | |
7496 | leency | 2097 | |
2098 | Компилятор отличает вещественное число от целого по наличию в нем |
||
2099 | точки. Начинаться вещественное число должно либо цифрой от 0 до 9, либо |
||
2100 | знаком минус. Необязательной частью вещественного числа является |
||
2101 | показатель степени. Показатель степени отделяется от числа символом e или |
||
2102 | E. Пробелы недопустимы. Вот примеры допустимого синтаксиса: |
||
2103 | |||
2104 | 0.98 |
||
2105 | -15.75 |
||
2106 | 3.14e2 |
||
2107 | 1.234567E-20 |
||
7544 | leency | 2108 | Return to contents. |
7496 | leency | 2109 | |
2110 | |||
7544 | leency | 2111 | |
7496 | leency | 2112 | 6.7.3 Диапазон допустимых значений. |
7544 | leency | 2113 | |
7496 | leency | 2114 | |
2115 | Вещественное число типа float может находиться в диапазоне от 3.37E38 |
||
2116 | до -3.37E38. Минимально близкое к нулю значение равняется 1.17E-38 и |
||
2117 | -1.17E-38. Записывать вещественное число одинарной точности более чем 8 |
||
2118 | цифрами не имеет смысла. Показатель степени может принимать значения от |
||
2119 | +38 до -38. |
||
7544 | leency | 2120 | Return to contents. |
7496 | leency | 2121 | |
2122 | |||
7544 | leency | 2123 | |
7496 | leency | 2124 | 6.7.4 Математические операции. |
7544 | leency | 2125 | |
7496 | leency | 2126 | |
2127 | Компилятор поддерживает 4 основных действия над переменными типа |
||
2128 | float: сложение, вычитание, умножение и деление. Поддерживается также |
||
2129 | инкремент (var++ - увеличение на 1), декремент (var-- - уменьшение на 1), |
||
2130 | смена знака (-var) и обмен значениями (var1 >< var2). Остальные |
||
2131 | математические операции будут реализованы либо уже реализованы во внешних |
||
2132 | библиотеках. При вычислении значения переменной float можно использовать |
||
2133 | и переменные других типов, они будут автоматически преобразованы в тип |
||
2134 | float. |
||
2135 | |||
2136 | ВНИМАНИЕ! Составные математические операции выполняются в том |
||
2137 | порядке, в котором они записаны, невзирая на правила арифметики. |
||
7544 | leency | 2138 | Return to contents. |
7496 | leency | 2139 | |
2140 | |||
7544 | leency | 2141 | |
7496 | leency | 2142 | 6.7.5 Преобразования типов. |
7544 | leency | 2143 | |
7496 | leency | 2144 | |
2145 | При математических операциях конечным итогом которых является |
||
2146 | переменная типа float, все операнды других типов перед вычислением будут |
||
2147 | преобразованы в тип float. При присваивании переменной типа float значения |
||
2148 | переменной другого типа оно также будет преобразовано в тип float. |
||
2149 | |||
2150 | Если при целочисленных вычислениях одним из операндов будет переменная |
||
2151 | типа float, то из него будет выделена целая часть, которая и примет |
||
2152 | участие в вычислениях. При присваивании целочисленной переменной значения |
||
2153 | переменной типа float, из нее также будет выделена целая часть, которая и |
||
2154 | будет присвоена целочисленной переменной. |
||
7544 | leency | 2155 | Return to contents. |
7496 | leency | 2156 | |
2157 | |||
7544 | leency | 2158 | |
7496 | leency | 2159 | 6.7.6 Операции сравнения. |
7544 | leency | 2160 | |
7496 | leency | 2161 | |
2162 | Если при операции сравнения левым операндом является переменная или |
||
2163 | выражение типа float, а правым является целочисленное значение, то |
||
2164 | целочисленное значение будет преобразовано в вещественный тип. Если же |
||
2165 | левым операндом является целочисленное выражение или переменная, а правым |
||
2166 | операндом значение типа float, то из правого операнда будет выделена целая |
||
2167 | часть, которая и примет участие в сравнении. |
||
7544 | leency | 2168 | Return to contents. |
7496 | leency | 2169 | |
2170 | |||
7544 | leency | 2171 | |
7496 | leency | 2172 | 6.7.7 Сравнение переменных типа float с 32-битным регистром. |
7544 | leency | 2173 | |
7496 | leency | 2174 | |
2175 | В регистрах могут содержаться знаковые, без знаковые и вещественные |
||
2176 | данные. По умолчанию считается, что в регистре находится без знаковое целое |
||
2177 | число. При сравнении переменных типа float с 32-битным регистром можно |
||
2178 | указывать тип данных содержащихся в регистре. Для этой цели можно |
||
2179 | использовать модификаторы: signed, unsigned, float. Примеры: |
||
2180 | |||
2181 | float f=1.0; |
||
2182 | |||
2183 | void PROC() |
||
2184 | { |
||
7544 | leency | 2185 | IF( f < signed ECX) //в регистре ECX находится знаковое число |
7496 | leency | 2186 | IF( unsigned EBX > f) //в регистре EBX находится без знаковое число |
2187 | IF( f == float EAX ) //в EAX находится число формата float |
||
2188 | } |
||
2189 | |||
2190 | ВНИМАНИЕ! При операции сравнения с участием переменой типа float, |
||
2191 | содержимое регистра AX будет разрушено. |
||
7544 | leency | 2192 | Return to contents. |
7496 | leency | 2193 | |
2194 | |||
7544 | leency | 2195 | |
7496 | leency | 2196 | 6.8 Указатели. |
7544 | leency | 2197 | |
7496 | leency | 2198 | |
2199 | В C-- сейчас указатели реализованы не в полном объеме. Поэтому многие |
||
2200 | вещи, которые возможны в обычных языках C, здесь будут недоступны. |
||
2201 | |||
2202 | Пример применения указателей в C--: |
||
2203 | |||
2204 | char *string[4]={"string1", "string2", "string3", 0}; //массив указателей |
||
2205 | char *str="string4"; |
||
2206 | |||
2207 | main() |
||
2208 | int i; |
||
2209 | char *tstr; |
||
2210 | { |
||
7544 | leency | 2211 | FOR(i=0; string[i]!=0; i++){ |
2212 | WRITESTR(string[i]); |
||
2213 | WRITELN(); |
||
2214 | } |
||
2215 | FOR(tstr=str;byte *tstr!=0; tstr++){ |
||
2216 | WRITE(byte *tstr); |
||
2217 | } |
||
7496 | leency | 2218 | } |
2219 | |||
2220 | Указатели можно использовать при передаче параметров процедурам, а в |
||
2221 | самих процедурах в качестве как локальных, так и параметрических |
||
2222 | переменных. Указатели можно также использовать в структурах. Можно |
||
2223 | использовать указатели на указатели. Введена поддержка указателей на |
||
2224 | процедуры: |
||
2225 | |||
2226 | void (*proc)(); //объявление указателя на процедуру |
||
2227 | |||
2228 | По умолчанию указатели на процедуру являются указателями на процедуру в |
||
2229 | стиле pascal, независимо от регистра, в котором написано имя процедуры и |
||
2230 | режима компиляции. Если Вам необходимо, чтобы был использован другой тип |
||
2231 | вызова, то его необходимо указать при объявлении указателя на процедуру. |
||
2232 | |||
2233 | При инициализации указателей компилятор не контролирует то, чем |
||
2234 | инициализируется указатель. Т.е. Вы можете указателю на char присвоить |
||
2235 | указатель на int или указателю на процедуру присвоить адрес переменной. |
||
2236 | Это может вызвать ошибку в работе программы. |
||
7544 | leency | 2237 | Return to contents. |
7496 | leency | 2238 | |
2239 | |||
7544 | leency | 2240 | |
7496 | leency | 2241 | 7. Адресация. |
2242 | |||
2243 | 7.1 Относительная адресация. |
||
7544 | leency | 2244 | |
7496 | leency | 2245 | |
2246 | Изначально индексный доступ к элементам в массивах любого типа в |
||
2247 | компиляторе осуществлялся побайтно, независимо от объявленного типа данных. |
||
2248 | Индексы ограничены форматом поля RM процессора 8086, таким образом, |
||
2249 | доступны только следующие форматы индексов (где индекс - значение |
||
2250 | 16-разрядной константы или постоянного выражения): |
||
2251 | |||
2252 | variable[index] |
||
2253 | variable[index+BX+SI] |
||
2254 | variable[index+BX+DI] |
||
2255 | variable[index+BP+SI] |
||
2256 | variable[index+BP+DI] |
||
2257 | variable[index+SI] |
||
2258 | variable[index+DI] |
||
2259 | variable[index+BP] |
||
2260 | variable[index+BX] |
||
2261 | |||
2262 | Начиная с версии 0.210, появилась возможность использовать в качестве |
||
2263 | индекса переменных типа char byte int word long dword. При этом |
||
2264 | доступ к элементам массива осуществляется в зависимости от объявленного типа |
||
2265 | массива. |
||
2266 | |||
2267 | Также начиная с версии 0.210 появилась возможность использовать в |
||
2268 | качестве индексных и базовых регистров при относительной адресации любые |
||
2269 | 32-битные регистры. |
||
2270 | |||
2271 | Если Вы для адресации к элементам массива будете использовать регистры и |
||
2272 | числовые константы, из которых можно получить поле RM для инструкций 8086 |
||
2273 | процессора или комбинацию полей RM BASE и SIB для 80386 процессора, то |
||
2274 | компилятор будет использовать эти регистры для генерации инструкции с этими |
||
2275 | полями. В результате Вы получите относительную побайтную адресацию к |
||
2276 | элементам массива. |
||
2277 | |||
2278 | Если же из этих регистров невозможно получить поля RM, BASE, SIB, |
||
2279 | или для адресации будет использована переменная, то компилятор сначала |
||
2280 | вычислит это выражение в регистр (E)SI или другой, подходящий регистр, а |
||
2281 | затем умножит содержимое этого регистра на разрядность Вашего массива. Таким |
||
2282 | образом, в этом случае вы будете иметь поэлементную адресацию в массиве. |
||
2283 | Пример: |
||
2284 | |||
2285 | AX = var [ 5 ]; |
||
2286 | AX = var [ BX + 5 ]; |
||
2287 | AX = var [ BX + CX ]; |
||
2288 | AX = var [ i ]; |
||
2289 | |||
2290 | Компилятор сгенерирует следующий код: |
||
2291 | test.c-- 7: AX=var[5]; |
||
2292 | 0100 A12501 mov ax,[125h] |
||
2293 | |||
2294 | test.c-- 8: AX=var[BX+5]; |
||
2295 | 0103 8B872501 mov ax,[bx+125h] |
||
2296 | |||
2297 | test.c-- 9: AX=var[BX+CX]; |
||
2298 | 0107 89DE mov si,bx |
||
2299 | 0109 01CE add si,cx |
||
2300 | 010B 01F6 add si,si |
||
2301 | 010D 8B842001 mov ax,[si+120h] |
||
2302 | |||
2303 | test.c-- 10: AX=var[i]; |
||
2304 | 0111 8B362201 mov si,[122h] |
||
2305 | 0115 01F6 add si,si |
||
2306 | 0117 8B842001 mov ax,[si+120h] |
||
2307 | |||
2308 | Как Вы видите, первые два выражения были преобразованы в одну |
||
2309 | ассемблерную инструкцию, и получилась побайтная адресация. В двух следующих |
||
2310 | выражениях получить одну ассемблерную инструкцию не удалось и компилятор |
||
2311 | применил для этих выражений поэлементную адресацию. |
||
2312 | |||
2313 | Такой двойственный подход реализован с целью сохранения совместимости |
||
2314 | новых возможностей с предыдущими. |
||
2315 | |||
2316 | Несмотря на кажущуюся для неискушенного пользователя путаницу, этот |
||
2317 | механизм легко понять и запомнить по следующему простому правилу: если Вы |
||
2318 | используете в качестве индекса только цифровое значение или регистр BX, SI, |
||
2319 | DI, BP или любой 32-битный регистр, то компилятор сгенерирует код с |
||
2320 | побайтной адресацией. Если же в качестве индекса будет использована |
||
2321 | переменная, то компилятор сгенерирует код с поэлементной адресацией. Если |
||
2322 | же Вы хорошо знакомы с ассемблером, то Вам не составит большого труда |
||
2323 | понять в каких случаях Вы получите побайтную, а в каких поэлементную |
||
2324 | адресацию. |
||
2325 | |||
2326 | Иногда требуется иметь побайтный доступ к элементам массива используя в |
||
2327 | качестве индекса переменную. Например |
||
2328 | |||
2329 | AX=var[i]; |
||
2330 | |||
2331 | Для этого выражения будет сгенерирована поэлементная адресация, а нам |
||
2332 | нужна побайтовая. Для этого можно написать так: |
||
2333 | |||
2334 | SI=i; |
||
2335 | AX=var[SI]; |
||
2336 | |||
2337 | Но можно это записать короче: |
||
2338 | |||
2339 | AX=DSWORD[#var+i]; |
||
2340 | |||
2341 | В обоих этих случаях Вы получите побайтную адресацию к элементам массива |
||
2342 | var. В первом варианте Вы сможете контролировать какой регистр будет |
||
2343 | использован в качестве индекса, а во втором варианте компилятор будет сам |
||
2344 | выбирать регистр для использования в качестве индекса. |
||
2345 | |||
2346 | Важно всегда помнить о двойственном подходе компилятора к вычислению |
||
2347 | адреса в массиве. Еще раз кратко: если Вы в массиве адресуетесь используя |
||
2348 | числовую константу или регистры BX,DI,SI,BP компилятор использует эти |
||
2349 | значения без изменения. Во всех других случаях будет коррекция значения в |
||
2350 | зависимости от типа массива. |
||
7544 | leency | 2351 | Return to contents. |
7496 | leency | 2352 | |
2353 | |||
7544 | leency | 2354 | |
7496 | leency | 2355 | 7.2 Абсолютная адресация. |
7544 | leency | 2356 | |
7496 | leency | 2357 | |
2358 | Абсолютная адресация также возможна. Действуют те же самые ограничения |
||
2359 | на индексы, что и при относительной адресации. |
||
2360 | |||
2361 | Вычисленный индекс будет абсолютен в сегменте, регистр которого указан. |
||
2362 | Можно указывать любой из регистров DS, CS, SS и ES. На процессорах 80386 и |
||
2363 | более новых можно указывать также регистры FS и GS. |
||
2364 | |||
2365 | Синтаксис - точно такой же, как и в относительной адресации, за |
||
2366 | исключением того, что указывается не переменная, а сегмент и тип данных. |
||
2367 | Могут применяться следующие указатели: |
||
2368 | |||
2369 | // адресация в сегменте данных |
||
2370 | DSBYTE [смещение] // адресует байт в сегменте DS |
||
2371 | DSWORD [смещение] // адресует слово в сегменте DS |
||
2372 | DSCHAR [смещение] // адресует char в сегменте DS |
||
2373 | DSINT [смещение] // адресует int в сегменте DS |
||
2374 | DSDWORD [смещение] // адресует dword в сегменте DS |
||
2375 | DSLONG [смещение] // адресует long в сегменте DS |
||
2376 | DSFLOAT [смещение] // адресует float в сегменте DS |
||
2377 | |||
2378 | // адресация в сегменте кода |
||
2379 | CSBYTE [смещение] // адресует байт в сегменте CS |
||
2380 | CSWORD [смещение] // адресует слово в сегменте CS |
||
2381 | CSCHAR [смещение] // адресует char в сегменте CS |
||
2382 | CSINT [смещение] // адресует int в сегменте CS |
||
2383 | CSDWORD [смещение] // адресует dword в сегменте CS |
||
2384 | CSLONG [смещение] // адресует long в сегменте CS |
||
2385 | CSFLOAT [смещение] // адресует float в сегменте CS |
||
2386 | |||
2387 | // адресация в сегменте стека |
||
2388 | SSBYTE [смещение] // адресует байт в сегменте SS |
||
2389 | SSWORD [смещение] // адресует слово в сегменте SS |
||
2390 | SSCHAR [смещение] // адресует char в сегменте SS |
||
2391 | SSINT [смещение] // адресует int в сегменте SS |
||
2392 | SSDWORD [смещение] // адресует dword в сегменте SS |
||
2393 | SSLONG [смещение] // адресует long в сегменте SS |
||
2394 | SSFLOAT [смещение] // адресует float в сегменте SS |
||
2395 | |||
2396 | // адресация в дополнительном сегменте данных |
||
2397 | ESBYTE [смещение] // адресует байт в сегменте ES |
||
2398 | ESWORD [смещение] // адресует слово в сегменте ES |
||
2399 | ESCHAR [смещение] // адресует char в сегменте ES |
||
2400 | ESINT [смещение] // адресует int в сегменте ES |
||
2401 | ESDWORD [смещение] // адресует dword в сегменте ES |
||
2402 | ESLONG [смещение] // адресует long в сегменте ES |
||
2403 | ESFLOAT [смещение] // адресует float в сегменте ES |
||
2404 | |||
2405 | // адресация в дополнительном сегменте 2 (80386) + |
||
2406 | FSBYTE [смещение] // адресует байт в сегменте FS |
||
2407 | FSWORD [смещение] // адресует слово в сегменте FS |
||
2408 | FSCHAR [смещение] // адресует char в сегменте FS |
||
2409 | FSINT [смещение] // адресует int в сегменте FS |
||
2410 | FSDWORD [смещение] // адресует dword в сегменте FS |
||
2411 | FSLONG [смещение] // адресует long в сегменте FS |
||
2412 | FSFLOAT [смещение] // адресует float в сегменте FS |
||
2413 | |||
2414 | // адресация в дополнительном сегменте 3 (80386) + |
||
2415 | GSBYTE [смещение] // адресуют байт в сегменте GS |
||
2416 | GSWORD [смещение] // адресуют слово в сегменте GS |
||
2417 | GSCHAR [смещение] // адресуют char в сегменте GS |
||
2418 | GSINT [смещение] // адресуют int в сегменте GS |
||
2419 | GSDWORD [смещение] // адресуют dword в сегменте GS |
||
2420 | GSLONG [смещение] // адресуют long в сегменте GS |
||
2421 | GSFLOAT [смещение] // адресует float в сегменте GS |
||
2422 | |||
2423 | Примеры: |
||
2424 | Загрузить в AL байт из ячейки с шестнадцатеричным адресом 0000:0417 |
||
2425 | ES = 0x0000; |
||
2426 | AL = ESBYTE [0x417]; |
||
2427 | |||
2428 | Переместить слово из ячейки с шестнадцатеричным адресом 2233:4455 |
||
2429 | в ячейку с шестнадцатеричным адресом A000:0002 |
||
2430 | $PUSH DS |
||
2431 | DS = 0x2233; |
||
2432 | ES = 0xA000; |
||
2433 | ESWORD [0x0002] = DSWORD [0x4455]; |
||
2434 | $POP DS |
||
2435 | |||
2436 | Сохранить вычисленное значение выражения X + 2, имеющее |
||
2437 | тип int в ячейке с шестнадцатеричным адресом FFFF:1234 |
||
2438 | ES = 0xFFFF; |
||
2439 | ESINT [0x1234] = X + 2; |
||
2440 | |||
2441 | Сохранить BX в сегменте стека по смещению 42: |
||
2442 | SSWORD [42] = BX; |
||
7544 | leency | 2443 | Return to contents. |
7496 | leency | 2444 | |
2445 | |||
7544 | leency | 2446 | |
7496 | leency | 2447 | 8. Работа с блоками данных. |
2448 | |||
2449 | 8.1 Структуры. |
||
2450 | |||
2451 | 8.1.1 Что такое структуры. |
||
7544 | leency | 2452 | |
7496 | leency | 2453 | |
2454 | Структура позволяет объединить в одном объекте совокупность значений, |
||
2455 | которые могут иметь различные типы. |
||
7544 | leency | 2456 | Return to contents. |
7496 | leency | 2457 | |
2458 | |||
7544 | leency | 2459 | |
7496 | leency | 2460 | 8.1.2 Синтаксис. |
7544 | leency | 2461 | |
7496 | leency | 2462 | |
2463 | struct [<тег>] { <список-объявлений-элементов> } |
||
2464 | <описатель>[,<описатель>...]; |
||
2465 | struct <тег> <описатель> [,<описатель>]; |
||
2466 | |||
2467 | Объявление структуры начинается с ключевого слова struct и имеет две |
||
2468 | формы записи. |
||
2469 | |||
2470 | В первой форме типы и имена элементов структуры специфицируются в |
||
2471 | списке-объявлений-элементов. Необязательный в данном случае тег - это |
||
2472 | идентификатор, который именует структурный тип, определенный данным |
||
2473 | списком объявлений элементов. описатель специфицирует либо переменную |
||
2474 | структурного типа, либо массив структур данного типа. |
||
2475 | |||
2476 | Вторая синтаксическая форма объявления использует тег структуры для |
||
2477 | ссылки на структурный тип, определенный где-то в другом месте программы. |
||
2478 | |||
2479 | Список объявлений элементов представляет собой последовательность из |
||
2480 | одной или более объявлений переменных. Каждая переменная, объявленная в |
||
2481 | этом списке, называется элементом структуры. |
||
2482 | |||
2483 | Элементы структуры запоминаются в памяти последовательно в том |
||
2484 | порядке, в котором они объявляются. Выравнивание элементов внутри |
||
2485 | структуры по умолчанию не производится. Но существует опция, включение |
||
2486 | которой в командную строку позволяет иметь выравнивание и внутри |
||
2487 | структуры. Сама структура выравнивается на четный адрес если включено |
||
2488 | выравнивание. |
||
2489 | |||
2490 | Примеры объявлений структур: |
||
2491 | |||
2492 | struct test |
||
2493 | { |
||
2494 | int a; |
||
2495 | char b[8]; |
||
2496 | long c; |
||
2497 | } rr, ff[4]; |
||
2498 | |||
2499 | В этом примере объявлены структура с именем rr и массив из 4 структур |
||
2500 | с именем ff. Всему набору переменных присвоено название (тег) test. Этот |
||
2501 | тег можно использовать для объявления других структур. Например: |
||
2502 | |||
2503 | struct test dd; |
||
2504 | |||
2505 | Здесь объявлена структура с именем dd, имеющая набор элементов |
||
2506 | описанных в теге test. |
||
2507 | |||
2508 | При объявлении структур с ранее объявленным тегом ключевое слово |
||
2509 | struct можно не писать. Т.е можно написать вот так: |
||
2510 | |||
2511 | test dd; |
||
7544 | leency | 2512 | Return to contents. |
7496 | leency | 2513 | |
2514 | |||
7544 | leency | 2515 | |
7496 | leency | 2516 | 8.1.3 Инициализация структур при объявлении. |
7544 | leency | 2517 | |
7496 | leency | 2518 | |
2519 | После объявления структуры ее элементы могут принимать произвольные |
||
2520 | значения. Что бы этого не было надо структуры проинициализировать. |
||
2521 | Инициализировать структуры при их объявлении можно только глобальные. C-- |
||
2522 | поддерживает несколько способов инициализации структур при их объявлении: |
||
2523 | |||
2524 | 1. Одним значением: |
||
2525 | |||
2526 | struct test dd=2; |
||
2527 | |||
2528 | В этом примере всем элементам структуры dd присваивается значение 2. |
||
2529 | |||
2530 | 2. Массивом значений: |
||
2531 | |||
2532 | struct test dd={1,2,,6}; |
||
2533 | |||
2534 | В этом примере первому элементу структуры dd присваивается значение 1, |
||
2535 | второму - 2, четвертому - 6. Пропущенным и не доинициализированным |
||
2536 | значениям будет присвоено 0 значение. |
||
2537 | |||
2538 | 3. Командой FROM: |
||
2539 | |||
2540 | struct test dd=FROM "file.dat"; |
||
2541 | |||
2542 | В этом примере на место где расположена структура dd при компиляции будет |
||
2543 | загружено содержимое файла |
||
2544 | структуры, то лишние байты будут загружены в код программы, но они не |
||
2545 | будут востребованы. Если размер файла меньше чем размер структуры, то |
||
2546 | недостающие байты структуры будут заполнены нулями. |
||
2547 | |||
2548 | 4. Командой EXTRACT: |
||
2549 | |||
2550 | struct test dd=EXTRACT "file.dat", 24, 10; |
||
2551 | |||
2552 | В этом примере на место где расположена структура dd при компиляции будет |
||
2553 | загружен фрагмент из файла file.dat длиной 10 байт со смещения 24. |
||
2554 | Недостающие байты будут заполнены нулями. |
||
7544 | leency | 2555 | Return to contents. |
7496 | leency | 2556 | |
2557 | |||
7544 | leency | 2558 | |
7496 | leency | 2559 | 8.1.4 Инициализация структуры при выполнении программы. |
7544 | leency | 2560 | |
7496 | leency | 2561 | |
2562 | При выполнении программы, кроме присвоения каждому элементу структуры |
||
2563 | значения, можно проинициализировать всю структуру присвоением ей числа или |
||
2564 | переменной. Примеры: |
||
2565 | |||
2566 | void proc() |
||
2567 | struct test aa[5],rr; |
||
2568 | int i; |
||
2569 | { |
||
2570 | aa[0]=0x12345678; |
||
2571 | aa[i]=int 0x12345678; |
||
2572 | aa=long 0x12345678; |
||
2573 | rr=i; |
||
2574 | |||
2575 | В первом примере память, занимаемая первой структурой массива из 5 |
||
2576 | структур, будет заполнена байтом 0x78 (по умолчанию). |
||
2577 | |||
2578 | Во втором примере память, занимаемая (i+1)-вой структурой массива из 5 |
||
2579 | структур, будет заполнена словом 0x5678. |
||
2580 | |||
2581 | В третьем примере память, занимаемая всем массивом из 5 структур, будет |
||
2582 | заполнена длинным словом 0x12345678. |
||
2583 | |||
2584 | В четвертом примере память, занимаемая структурой rr, будет заполнена |
||
2585 | содержимым переменной i. |
||
2586 | |||
2587 | Можно также копировать содержимое одной структуры в другую. Например: |
||
2588 | |||
2589 | rr=aa[2]; |
||
2590 | |||
2591 | Будет скопировано содержимое третьей структуры массива структур aa в |
||
2592 | структуру rr. |
||
7544 | leency | 2593 | Return to contents. |
7496 | leency | 2594 | |
2595 | |||
7544 | leency | 2596 | |
7496 | leency | 2597 | 8.1.5 Операции с элементами структур. |
7544 | leency | 2598 | |
7496 | leency | 2599 | |
2600 | С элементами структур можно выполнять все те операции, которые |
||
2601 | доступны для переменных соответствующего типа. Например: Объявлена |
||
2602 | структура: |
||
2603 | |||
2604 | struct test |
||
2605 | { |
||
2606 | int a; |
||
2607 | char b[8]; |
||
2608 | long c; |
||
2609 | } rr[3]; |
||
2610 | Пример допустимого синтаксиса: |
||
2611 | rr.a = rr.b[i] * rr[1].c + i ; |
||
2612 | |||
2613 | Примечание: |
||
2614 | При операциях с элементами массива структур и с индексированными |
||
2615 | элементами, в которых в качестве индекса или номера структуры используется |
||
2616 | переменная, компилятор может использовать регистры SI и DI, а в некоторых |
||
2617 | ситуациях (например: rr[i].b[j] >< rr[i+1].b[j+2] ) будет задействован и |
||
2618 | регистр DX. |
||
2619 | |||
2620 | Для отдельных элементов структуры, можно получать их адрес, размер |
||
2621 | и смещение в теге структуры. Вот пример: |
||
2622 | |||
2623 | struct AA //объявление тега структуры |
||
2624 | { |
||
2625 | word a[3]; // первый элемент структуры |
||
2626 | char b; // второй элемент структуры |
||
2627 | long c; // третий элемент структуры |
||
2628 | }; |
||
2629 | |||
7544 | leency | 2630 | struct BB //тег второй структуры |
7496 | leency | 2631 | { |
7544 | leency | 2632 | word aa; // первый элемент |
2633 | AA bb; // второй элемент - вложенная структура |
||
2634 | }ss; // объявляем структуру с тегом BB |
||
7496 | leency | 2635 | |
2636 | void proc() |
||
2637 | { |
||
2638 | AX=#ss.bb.b; // получить адрес элемента b структуры bb в структуре ss |
||
2639 | AX=#BB.bb.b; // получить смещение этого же элемента в теге BB |
||
7544 | leency | 2640 | AX=sizeof(ss.bb); // получить размер элемента bb в структуре ss |
2641 | AX=sizeof(BB.bb); // получить размер элемента bb в теге BB |
||
7496 | leency | 2642 | } |
7544 | leency | 2643 | Return to contents. |
7496 | leency | 2644 | |
2645 | |||
7544 | leency | 2646 | |
7496 | leency | 2647 | 8.1.6 Вложенные структуры. |
7544 | leency | 2648 | |
7496 | leency | 2649 | |
2650 | При объявлении тегов структур можно использовать теги других, |
||
2651 | объявленных ранее структур. Пример вложенных структур: |
||
2652 | |||
2653 | struct RGB |
||
2654 | { |
||
2655 | byte Red; |
||
2656 | byte Green; |
||
2657 | byte Blue; |
||
2658 | byte Reserved; |
||
2659 | }; |
||
2660 | |||
2661 | struct BMPINFO |
||
2662 | { |
||
2663 | struct BMPHEADER header; //описание этой структуры пропущено |
||
2664 | struct RGB color[256]; |
||
2665 | }info; |
||
2666 | |||
2667 | Предположим Вам нужно получить содержимое переменной Red десятого |
||
2668 | элемента color. Это можно будет записать так: |
||
2669 | |||
2670 | AL=info.color[10].Red; |
||
2671 | |||
2672 | Но существует одно ограничение использования вложенных структур в C--. |
||
2673 | Это невозможность использования переменной в качестве индекса более одного |
||
2674 | раза при обращении к многоэкземплярным структурам. Поясним это на примере: |
||
2675 | |||
2676 | struct ABC |
||
2677 | { |
||
2678 | int a; |
||
2679 | int b; |
||
2680 | int c; |
||
2681 | }; |
||
2682 | |||
2683 | struct |
||
2684 | { |
||
2685 | struct ABC first[4]; //4 экземпляра структуры ABC |
||
2686 | int d; |
||
2687 | }second[4]; |
||
2688 | |||
2689 | int i,j; |
||
2690 | |||
2691 | void proc() |
||
2692 | { |
||
2693 | AX=second[i].first[j].a; //такая запись вызовет сообщение об ошибка, так |
||
2694 | //как переменная использовалась в двух местах |
||
2695 | AX=second[2].first[j].a; //а этот синтаксис допустим. |
||
2696 | AX=second[i].first[3].a; |
||
2697 | } |
||
7544 | leency | 2698 | Return to contents. |
7496 | leency | 2699 | |
2700 | |||
7544 | leency | 2701 | |
7496 | leency | 2702 | 8.1.7 Отображение тега структуры на блок памяти. |
7544 | leency | 2703 | |
7496 | leency | 2704 | |
2705 | Отображение тега структуры на блок памяти является альтернативой |
||
2706 | указателям на структуры. |
||
2707 | |||
2708 | Альтернативный способ использования указателей на структуры позволит |
||
2709 | Вам самим выбрать регистр, в котором будет хранится адрес структуры и |
||
2710 | самим следить за его сохранностью и по мере необходимости восстанавливать |
||
2711 | его содержимое. |
||
2712 | |||
2713 | Объяснить, как использовать отображение тега структуры на память, |
||
2714 | наверное, будет проще на примере: |
||
2715 | |||
2716 | struct AA //объявление тега структуры |
||
2717 | { |
||
2718 | word a[3]; // первый элемент структуры |
||
2719 | char b; // второй элемент структуры |
||
2720 | long c; // третий элемент структуры |
||
2721 | }; |
||
2722 | |||
2723 | byte buf[256]; //блок памяти, на который будет отображен тег структуры |
||
2724 | |||
2725 | void proc1() |
||
2726 | { |
||
2727 | ... |
||
2728 | proc2 ( #buf ); // вызов процедуры с передачей ей в качестве параметра |
||
2729 | // адреса блока памяти |
||
2730 | ... |
||
2731 | } |
||
2732 | |||
2733 | long proc2 (unsigned int pointer_to_mem) |
||
2734 | { |
||
2735 | int i; |
||
2736 | BX=pointer_to_mem; // в BX загрузим адрес блока памяти |
||
2737 | FOR(i=0; i<3; i++){ // в массив элемента a записать -1 |
||
2738 | BX.AA.a[i]=-1; |
||
2739 | } |
||
2740 | BX.AA.b=0; |
||
2741 | ES:BX.AA.c=EAX; |
||
2742 | return BX.AA.c; // вернуть содержимое элемента c |
||
2743 | } |
||
2744 | |||
2745 | В 16-битном режиме для хранения адреса структуры можно использовать |
||
2746 | регистры: BX,DI,SI,BP. Но лучше для этого использовать регистр BX. |
||
2747 | Регистры DI и SI может использовать компилятор при вычислении адреса |
||
2748 | многоэлементных объектов. Регистр BP компилятор использует для работы с |
||
2749 | локальными и параметрическими переменными. В 32-битном режиме можно |
||
2750 | использовать любой кроме ESP и EBP регистр, а регистры EDI и ESI надо |
||
2751 | использовать осторожно. |
||
7544 | leency | 2752 | Return to contents. |
7496 | leency | 2753 | |
2754 | |||
7544 | leency | 2755 | |
7496 | leency | 2756 | 8.1.8 Битовые поля структур. |
7544 | leency | 2757 | |
7496 | leency | 2758 | |
2759 | Битовые поля структур используются для экономии памяти, поскольку |
||
2760 | позволяют плотно упаковать значения, и для организации удобного доступа к |
||
2761 | регистрам внешних устройств, в которых различные биты могут иметь |
||
2762 | самостоятельное функциональное назначение. |
||
2763 | |||
2764 | Объявление битового поля имеет следующий синтаксис: |
||
2765 | |||
2766 | <тип> [<идентификатор>]:<константа>; |
||
2767 | |||
2768 | или на примере: |
||
2769 | |||
2770 | int var:5; //объявление битового поля размером 5 бит с именем var |
||
2771 | |||
2772 | Битовое поле состоит из некоторого числа битов, которое задается |
||
2773 | числовым выражением константа. Его значение должно быть целым |
||
2774 | положительным числом и его значение не должно превышать числа разрядов, |
||
2775 | соответствующие типу определяемого битового поля. В C-- битовые поля |
||
2776 | могут содержать только без знаковые значения. Нельзя использовать массивы |
||
2777 | битовых полей, указатели на битовые поля. |
||
2778 | |||
2779 | идентификатор именует битовое поле. Его наличие необязательно. |
||
2780 | Неименованное битовое поле означает пропуск соответствующего числа битов |
||
2781 | перед размещением следующего элемента структуры. Неименованное битовое |
||
2782 | поле, для которого указан нулевой размер, имеет специальное назначение: |
||
2783 | оно гарантирует, что память для следующего битового поля будет начинаться |
||
2784 | на границе того типа, который задан для неименованного битового поля. |
||
2785 | Т.е. будет произведено выравнивание битового поля на 8/16/32 бита. |
||
2786 | |||
2787 | В C-- все битовые поля упаковываются одно за другим независимо от |
||
2788 | границ типа идентификаторов. Если последующее поле не является битовым |
||
2789 | полем, то оставшиеся до границы байта биты не будут использованы. |
||
2790 | Максимальный размер битового поля равен 32 бита для типа dword/long, 16 |
||
2791 | бит для типа word/int и 8 бит для типа byte/char. Битовые поля можно |
||
2792 | объединять, т.е. использовать их в операторе union. sizeof |
||
2793 | примененный к битовому полю вернет размер этого поля в битах. При |
||
2794 | использовании битового поля, его содержимое будет расширятся в регистр |
||
2795 | как без знаковое целое число. |
||
7544 | leency | 2796 | Return to contents. |
7496 | leency | 2797 | |
2798 | |||
7544 | leency | 2799 | |
7496 | leency | 2800 | 8.2 Объединения. |
7544 | leency | 2801 | |
7496 | leency | 2802 | |
2803 | Объединения позволяют в разные моменты времени хранить в одном объекте |
||
2804 | значения различного типа. |
||
2805 | |||
2806 | Память, которая выделяется под объединение, определяется размером |
||
2807 | наиболее длинного из элементов объединения. Все элементы объединения |
||
2808 | размещаются в одной и той же области памяти с одного и того же адреса. |
||
2809 | Значение текущего элемента объединения теряется, когда другому элементу |
||
2810 | объединения присваивается значение. |
||
2811 | |||
2812 | В C-- реализованы так называемые анонимные объединения. Т.е. |
||
2813 | объединениям не присваивается имя, а обращение к элементам объединения |
||
2814 | происходит как к обычной переменной. Пример: |
||
2815 | |||
2816 | union |
||
2817 | { |
||
2818 | dword regEAX; |
||
2819 | word regAX; |
||
2820 | byte regAL; |
||
2821 | }; // объявили, что 3 переменные расположены по одному и тому же |
||
2822 | // физическому адресу |
||
2823 | |||
2824 | void test() |
||
2825 | { |
||
7544 | leency | 2826 | regEAX = 0x2C; |
2827 | BL = regAL; //в регистре BL окажется значение 0x2C |
||
7496 | leency | 2828 | } |
2829 | |||
2830 | Объединять можно переменные различных типов, массивы, строковые |
||
2831 | переменные и структуры. Объединения могут быть глобальными и локальными, а |
||
2832 | также располагаться внутри структур (пока в объединениях внутри структур |
||
2833 | нельзя использовать структуры). Глобальные объединения могут быть |
||
2834 | инициализированными и неинициализированными. Чтобы получить |
||
2835 | инициализированное объединение достаточно проинициализировать лишь первый |
||
2836 | элемент объединения. Если же первый элемент объединения не инициализирован, |
||
2837 | а следующие элементы инициализированы, то это вызовет сообщение компилятора |
||
2838 | об ошибке. |
||
7544 | leency | 2839 | Return to contents. |
7496 | leency | 2840 | |
2841 | |||
7544 | leency | 2842 | |
7496 | leency | 2843 | 8.3 Команды 'FROM' и 'EXTRACT'. |
7544 | leency | 2844 | |
7496 | leency | 2845 | |
2846 | В C-- есть очень оригинальные команды, которых нет в других языках. Это |
||
2847 | FROM и EXTRACT. |
||
2848 | |||
2849 | Команда FROM имеет синтаксис: |
||
2850 | |||
2851 | <тип_переменной> <имя_переменной> = FROM <имя_файла>; |
||
2852 | |||
2853 | Встретив эту команду при компиляции, компилятор загрузит в выходной |
||
2854 | файл содержимое файла имя_файла, а имя_переменной будет идентификатором |
||
2855 | начала загруженного кода. Вот пример использования этой команды из файла |
||
2856 | tinydraw.c--: |
||
2857 | |||
2858 | byte palette[PALSIZE] = FROM "TINYDRAW.PAL"; // buffer for palette |
||
2859 | |||
2860 | Команда EXTRACT имеет синтаксис: |
||
2861 | |||
2862 | <тип_переменной> <имя_переменной> = EXTRACT <имя_файла>, <начало>, <длина>; |
||
2863 | |||
2864 | Встретив эту команду при компиляции, компилятор загрузит в выходной |
||
2865 | файл из файла имя_файла число байт равное длина со смещения начало, а |
||
2866 | имя_переменной будет идентификатором начала загруженного кода. Вот пример |
||
2867 | использования этой команды: |
||
2868 | |||
2869 | byte LIT128 = EXTRACT "8X16.FNT", 16*128, 16; |
||
2870 | byte LIT130 = EXTRACT "8X16.FNT", 16*130, 16; |
||
7544 | leency | 2871 | Return to contents. |
7496 | leency | 2872 | |
2873 | |||
7544 | leency | 2874 | |
7496 | leency | 2875 | 9. Операторы. |
2876 | |||
2877 | 9.1 Условные инструкции. |
||
7544 | leency | 2878 | |
7496 | leency | 2879 | |
2880 | Условные инструкции, при помощи которых осуществляется ветвление, такие |
||
2881 | же как в C. |
||
2882 | |||
2883 | C-- имеет две инструкции ветвления. if и IF. |
||
2884 | |||
2885 | if делает близкий условный переход, а IF делает короткий |
||
2886 | (8-разрядный) условный переход. IF выполняется быстрее и может экономить |
||
2887 | до 3 байт в размере кода, но может осуществлять переходы только в пределах |
||
2888 | 127 байтов кода. |
||
2889 | |||
2890 | Условные инструкции, как и в C, могут сопровождаться, как одиночной |
||
2891 | командой, так и блоком из нескольких команд, заключенных в фигурные скобки |
||
2892 | { и }. Условные инструкции имеют те же ограничения, что и условные |
||
2893 | выражения. |
||
2894 | |||
2895 | Если за инструкцией IF следует больше чем 127 байтов кода, транслятор |
||
2896 | выдаст следующее сообщение об ошибке: |
||
2897 | |||
2898 | IF jump distance too far, use if. |
||
2899 | |||
2900 | Это можно просто исправить, заменив в этом месте инструкцию IF на if. |
||
2901 | |||
2902 | Команды else и ELSE используются точно так же, как в языке C. |
||
2903 | Отличие их в том, что ELSE имеет ограничение адреса перехода 127 байт, |
||
2904 | такое же как IF. else генерирует код на 1 байт длиннее, чем ELSE. |
||
2905 | |||
2906 | Команды IF и else, а также if и ELSE могут свободно смешиваться |
||
2907 | как в следующем примере: |
||
2908 | |||
2909 | if( x == 2 ) |
||
2910 | WRITESTR("Two"); |
||
2911 | ELSE{ WRITESTR("not two."); |
||
2912 | printmorestuff(); |
||
2913 | } |
||
2914 | |||
2915 | Если за инструкцией ELSE следует больше чем 127 байтов кода, |
||
2916 | транслятор выдаст следующее сообщение об ошибке: |
||
2917 | |||
2918 | ELSE jump distance too far, use else. |
||
2919 | |||
2920 | Это можно просто исправить, заменив в этом месте инструкцию ELSE на |
||
2921 | else. |
||
7544 | leency | 2922 | Return to contents. |
7496 | leency | 2923 | |
2924 | |||
7544 | leency | 2925 | |
7496 | leency | 2926 | 9.2 Циклы do{} while. |
7544 | leency | 2927 | |
7496 | leency | 2928 | |
2929 | В таком цикле блок кода, составляющий тело цикла, будет повторяться, |
||
2930 | пока условное выражение имеет значение истинно. |
||
2931 | |||
2932 | Истинность условного выражения проверяется после выполнения тела цикла, |
||
2933 | поэтому блок кода будет выполнен, по крайней мере, один раз. |
||
2934 | |||
2935 | Пример do {} while цикла, в котором тело будет исполнено пять раз: |
||
2936 | |||
2937 | count = 0; |
||
2938 | do { |
||
2939 | count++; |
||
2940 | WRITEWORD(count); |
||
2941 | WRITELN(); |
||
2942 | } while (count < 5); |
||
2943 | |||
2944 | Условное выражение в do {} while инструкции должно соответствовать тем же |
||
2945 | правилам, что и в инструкциях IF и if. |
||
7544 | leency | 2946 | Return to contents. |
7496 | leency | 2947 | |
2948 | |||
7544 | leency | 2949 | |
7496 | leency | 2950 | 9.3 Циклы loop, LOOPNZ, loopnz. |
7544 | leency | 2951 | |
7496 | leency | 2952 | |
2953 | Циклы loop повторяют блок кода, пока определенная переменная или |
||
2954 | регистр, выполняющие роль счетчика цикла, содержат значение, отличное от |
||
2955 | нуля. В конце выполнения блока кода, составляющего тело цикла, указанная |
||
2956 | переменная или регистр - уменьшается на 1, а затем проверяется на равенство |
||
2957 | нулю. Если переменная (или регистр) не равна нулю, тело цикла будет |
||
2958 | выполнено снова, и процесс повторится. |
||
2959 | |||
2960 | Пример использования цикла loop в котором в качестве счетчика цикла |
||
2961 | использована переменная: |
||
2962 | |||
2963 | count = 5; |
||
2964 | loop( count ) |
||
2965 | {WRITEWORD(count); |
||
2966 | WRITELN(); |
||
2967 | } |
||
2968 | |||
2969 | Наибольший эффект дает использование регистра CX для циклов с небольшим |
||
2970 | телом, поскольку в этом случае компилятором генерируется цикл с применением |
||
2971 | машинной команды LOOP. |
||
2972 | |||
2973 | Если перед стартом счетчик циклов содержит нулевое значение, команды |
||
2974 | тела цикла будут выполнены максимальное число раз для диапазона переменной |
||
2975 | (256 раз для 8-битного счетчика (переменной типа byte или char), 65536 для |
||
2976 | 16-битного счетчика (переменной типа word или int), и 4294967296 для |
||
2977 | 32-битного счетчика (переменной типа dword или long). |
||
2978 | |||
2979 | В следующем примере цикл будет выполнен 256 раз: |
||
2980 | |||
2981 | BH = 0; |
||
2982 | loop (BH) |
||
2983 | { |
||
2984 | } |
||
2985 | |||
2986 | Если в команде не указано никакого счетчика цикла, цикл будет |
||
2987 | продолжаться бесконечно. |
||
2988 | |||
2989 | Следующий пример будет непрерывно выводить символ звездочки (*) на |
||
2990 | экран: |
||
2991 | |||
2992 | loop() |
||
2993 | WRITE('*'); |
||
2994 | |||
2995 | Программист, если хочет, может использовать или изменять значение |
||
2996 | переменной счетчика цикла внутри цикла. |
||
2997 | |||
2998 | Например, следующий цикл выполнится только 3 раза: |
||
2999 | |||
3000 | CX = 1000; |
||
3001 | loop( CX ) |
||
3002 | { |
||
3003 | IF( CX > 3 ) |
||
3004 | CX = 3; |
||
3005 | } |
||
3006 | |||
3007 | Цикл можно также прервать оператором разрыва BREAK или break. Вот |
||
3008 | тот же пример с использованием BREAK: |
||
3009 | |||
3010 | CX = 1000; |
||
3011 | loop( CX ) |
||
3012 | { |
||
3013 | IF( CX > 3 ) |
||
3014 | BREAK; |
||
3015 | } |
||
3016 | |||
3017 | Циклы LOOPNZ/loopnz отличаются от цикла loop, тем, что перед входом |
||
3018 | в цикл проверяется равенство нулю аргумента цикла. Если аргумент равен |
||
3019 | нулю, то тело цикла ни разу не выполнится (в цикле loop в этом случае |
||
3020 | тело цикла выполнится максимальное число раз). Цикл LOOPNZ получается |
||
3021 | максимально эффективным при оптимизации на размер кода, если в качестве |
||
3022 | параметра-счетчика используется регистр CX/ECX. При этом компилятор |
||
3023 | использует ассемблерные инструкции JCXZ/JECXZ и LOOP. |
||
7544 | leency | 3024 | Return to contents. |
7496 | leency | 3025 | |
3026 | |||
7544 | leency | 3027 | |
7496 | leency | 3028 | 9.4 Цикл while, WHILE. |
7544 | leency | 3029 | |
7496 | leency | 3030 | |
3031 | Синтаксис: |
||
3032 | while(<выражение>) |
||
3033 | <оператор> |
||
3034 | |||
3035 | Цикл выполняется до тех пор, пока значение выражения не станет |
||
3036 | ложным. Вначале вычисляется выражение. Если выражение изначально ложно, |
||
3037 | то тело оператора while вообще не выполняется и управление сразу |
||
3038 | передается на следующий оператор программы. |
||
3039 | |||
3040 | Цикл WHILE аналогичен циклу while, но при этом генерируется код на |
||
3041 | 3 байта короче. Размер сгенерированного кода в цикле WHILE должен быть |
||
3042 | меньше 127 байт. |
||
3043 | |||
3044 | Примеры: |
||
7544 | leency | 3045 | while ( i < 20 ){ |
3046 | WRITEWORD(i); |
||
3047 | i++; |
||
3048 | } |
||
7496 | leency | 3049 | |
7544 | leency | 3050 | WHILE (i < 20 ) @WRITEWORD(i); //цикл либо будет бесконечным либо не |
7496 | leency | 3051 | //выполнится ни разу |
7544 | leency | 3052 | Return to contents. |
7496 | leency | 3053 | |
3054 | |||
7544 | leency | 3055 | |
7496 | leency | 3056 | 9.5 Цикл for, FOR. |
7544 | leency | 3057 | |
7496 | leency | 3058 | |
3059 | Синтаксис: |
||
3060 | for ([<начальное выражение>]; [<условие>]; [<приращение>]) |
||
3061 | <оператор> |
||
3062 | |||
3063 | Цикл for выполняется до тех пор, пока значение условия не станет |
||
3064 | ложным. Если условие изначально ложно, то тело оператора for вообще не |
||
3065 | выполняется и управление сразу передается на следующий оператор программы. |
||
3066 | Начальное выражение и приращение обычно используются для инициализации |
||
3067 | и модификации параметров цикла. |
||
3068 | |||
3069 | Первым шагом при выполнении for является вычисление начального |
||
3070 | выражения, если оно имеется. Затем вычисляется условие и производится |
||
3071 | его оценка следующим образом: |
||
3072 | |||
3073 | 1) Если условие истинно, то выполняется тело оператора. Затем |
||
3074 | вычисляется приращение (если оно есть), и процесс повторяется. |
||
3075 | |||
3076 | 2) Если условие опущено, то его значение принимается за истину. В |
||
3077 | этом случае цикл for представляет бесконечный цикл, который может |
||
3078 | завершиться только при выполнении в его теле операторов break, goto, |
||
3079 | return. |
||
3080 | |||
3081 | 3) Если условие ложно, то выполнение цикла for заканчивается и |
||
3082 | управление передается следующему оператору. |
||
3083 | |||
3084 | Цикл FOR аналогичен циклу for, но при этом генерируется код на 3 |
||
3085 | байта короче. Размер сгенерированного кода в цикле FOR должен быть меньше |
||
3086 | 127 байт. |
||
3087 | |||
3088 | Примеры: |
||
7544 | leency | 3089 | for(i=0;i<5;i++){ |
3090 | WRITESTR("СТРОКА "); |
||
3091 | WRITEWORD(i); |
||
3092 | WRITELN(); |
||
3093 | } |
||
7496 | leency | 3094 | |
3095 | Число начальных выражений и число приращений не ограничено. Каждый |
||
3096 | оператор в начальных выражениях и приращениях должен разделяться |
||
3097 | запятой. Пример: |
||
3098 | |||
3099 | for ( a=1, b=2 ; a<5 ; a++, b+=a ) {... |
||
3100 | |||
3101 | Также есть возможность логического объединения условий. Объединять |
||
3102 | можно до 32 условий. Каждое объединяемое условие должно быть заключено в |
||
3103 | скобки. Пример: |
||
3104 | |||
3105 | for ( a=0 ; (a>=0) && (a<10) ; a++ ){... |
||
7544 | leency | 3106 | Return to contents. |
7496 | leency | 3107 | |
3108 | |||
7544 | leency | 3109 | |
7496 | leency | 3110 | 9.6 Оператор переключатель switch. |
7544 | leency | 3111 | |
7496 | leency | 3112 | |
3113 | Синтаксис: |
||
3114 | switch(<выражение>){ |
||
3115 | case <константа>: |
||
3116 | <оператор> |
||
7544 | leency | 3117 | ... |
7496 | leency | 3118 | case <константа>: |
3119 | <оператор> |
||
7544 | leency | 3120 | ... |
7496 | leency | 3121 | ... |
3122 | default: |
||
3123 | <оператор> |
||
3124 | } |
||
3125 | Оператор переключатель switch предназначен для выбора одного из |
||
3126 | нескольких альтернативных путей выполнения программы. Выполнение начинается |
||
3127 | с вычисления значения выражения. После этого управление передается одному |
||
3128 | из операторов тела переключателя. В теле переключателя содержатся |
||
3129 | конструкции: case константа:, которые синтаксически представляют собой |
||
3130 | метки операторов. Оператор, получающий управление, - это тот оператор, |
||
3131 | значение константы которого совпадают со значением выражения |
||
3132 | переключателя. Значение константы должно быть уникальным. |
||
3133 | |||
3134 | Выполнение тела оператора-переключателя switch начинается с выбранного |
||
3135 | таким образом оператора, и продолжается до конца тела или до тех пор, пока |
||
3136 | какой-либо оператор не передаст управление за пределы тела. |
||
3137 | |||
3138 | Оператор, следующий за ключевым словом default, выполняется, если ни |
||
3139 | одна из констант не равна значению выражения. Если default опущено, то |
||
3140 | ни один оператор в теле переключателя не выполняется, и управление |
||
3141 | передается на оператор, следующий за switch. |
||
3142 | |||
3143 | Для выхода из тела переключателя обычно используется оператор разрыва |
||
3144 | break (BREAK). |
||
3145 | |||
3146 | Пример: |
||
3147 | switch (i){ |
||
3148 | case 'A': |
||
3149 | WRITE(i); |
||
3150 | i++; |
||
3151 | BREAK; |
||
3152 | case 32: |
||
3153 | WRITE('_'); |
||
3154 | i++; |
||
3155 | BREAK; |
||
3156 | default: |
||
3157 | WRITE('i'); |
||
3158 | } |
||
3159 | |||
3160 | Оператор switch сейчас в компиляторе может реализовываться трем |
||
3161 | способами: двухтабличным, табличным и методом последовательных проверок. |
||
3162 | |||
3163 | Табличный метод является самым быстрым, а при большом числе операторов |
||
3164 | case и при незначительной разнице между максимальным и минимальным |
||
3165 | значениями case он еще может быть и более компактным. Но у него есть и |
||
3166 | недостатки: в 16-битном режиме компилятор всегда использует регистр BX, а в |
||
3167 | 32-битном режиме, если операндом switch является регистр, то его значение |
||
3168 | будет разрушено. |
||
3169 | |||
3170 | В методе последовательных проверок блок сравнений находится в начале |
||
3171 | тела оператора switch, это позволяет избавиться от 1-2 лишних jmp. Но |
||
3172 | компилятор не может определить, какой тип перехода использовать при |
||
3173 | проверке значений case. Это будет Вашей заботой. Если размер кода от |
||
3174 | начала тела оператора switch до места расположения оператора case |
||
3175 | меньше 128 байт, можно использовать короткий переход. В этом случае Вы |
||
3176 | можете указать оператор CASE, что приведет к генерации более компактного |
||
3177 | кода. Компилятор в предупреждениях будет Вам подсказывать о возможности |
||
3178 | использования операторов CASE. Использование оператора CASE в случаях, |
||
3179 | когда размер блока кода более 128 байт приведет к выдаче компилятором |
||
3180 | сообщения об ошибке. |
||
3181 | |||
3182 | При двухтабличном методе создаются две таблицы - таблица адресов входа в |
||
3183 | тело оператора switch/SWITCH и таблица значений case. Генерируется |
||
3184 | процедура сравнения входного значения со значениями во второй таблице. Если |
||
3185 | есть совпадение, то делается переход по адресу из второй таблицы. Этот |
||
3186 | метод является самым медленным, но при большом числе значений case (более |
||
3187 | 15) он становится самым компактным. |
||
3188 | |||
3189 | При оптимизации кода на размер, компилятор предварительно вычисляет |
||
3190 | размер кода, который может быть получен всеми методами и реализует самый |
||
3191 | компактный. При оптимизации на скорость преимущество отдается табличному |
||
3192 | методу, если размер таблицы получается не слишком большим. |
||
3193 | |||
3194 | Для оператора switch введена также и короткая его форма - SWITCH. |
||
3195 | Ее можно применять в случае, если размер блока кода между началом тела |
||
3196 | оператора и оператором default (если он отсутствует, то концом тела |
||
3197 | оператора switch) меньше 128 байт. О возможности использования короткой |
||
3198 | формы компилятор будет сообщать в предупреждениях. |
||
3199 | |||
3200 | Для оператора case/CASE, который может использоваться только в теле |
||
3201 | блока оператора switch/SWITCH, можно указывать диапазон значений. Сначала |
||
3202 | надо указывать меньшее значение, затем после многоточия большее. Пример: |
||
3203 | |||
3204 | switch(AX){ |
||
3205 | case 1...5: |
||
3206 | WRITESTR("Range AX from 1 to 5"); |
||
3207 | BREAK; |
||
3208 | }; |
||
3209 | |||
3210 | Раньше Вам бы пришлось писать более громоздкую конструкцию: |
||
3211 | |||
3212 | switch(AX){ |
||
3213 | case 1: |
||
3214 | case 2: |
||
3215 | case 3: |
||
3216 | case 4: |
||
3217 | case 5: |
||
3218 | WRITESTR("Range AX from 1 to 5"); |
||
3219 | BREAK; |
||
3220 | }; |
||
3221 | |||
3222 | Кроме того, что новый формат записи более компактен и более читабелен, |
||
3223 | но еще при этом компилятор создает более компактный и быстрый код. |
||
7544 | leency | 3224 | Return to contents. |
7496 | leency | 3225 | |
3226 | |||
7544 | leency | 3227 | |
7496 | leency | 3228 | 9.7 Оператор перехода goto, GOTO. |
7544 | leency | 3229 | |
7496 | leency | 3230 | |
3231 | Синтаксис: |
||
3232 | goto <метка>; |
||
7544 | leency | 3233 | . |
3234 | . |
||
3235 | . |
||
7496 | leency | 3236 | <метка>: |
3237 | |||
3238 | Оператор перехода goto передает управление на оператор помеченный |
||
3239 | меткой. Аналогом в ассемблере оператору goto является команда jmp near. |
||
3240 | Аналогом в ассемблере оператору GOTO является команда jmp short. |
||
7544 | leency | 3241 | Return to contents. |
7496 | leency | 3242 | |
3243 | |||
7544 | leency | 3244 | |
7496 | leency | 3245 | 9.8 Оператор разрыва break, BREAK. |
7544 | leency | 3246 | |
7496 | leency | 3247 | |
3248 | Оператор разрыва break прерывает выполнение операторов do-while, |
||
3249 | for, switch, while, loop, loopnz, LOOPNZ. Он может содержаться |
||
3250 | только в теле этих операторов. Управление передается оператору, следующему |
||
3251 | за прерванным циклом. |
||
3252 | |||
3253 | Оператор BREAK аналогичен break, но при этом генерируется код на 1 |
||
3254 | байт короче. Размер сгенерированного кода от места где применяется BREAK |
||
3255 | до конца цикла должен быть меньше 127 байт. |
||
3256 | |||
3257 | Примеры: |
||
7544 | leency | 3258 | FOR (i=0; ; i++){ |
3259 | FOR(j=0; j < WIDTH; j++){ |
||
3260 | IF(i==5)BREAK; |
||
3261 | } |
||
3262 | IF(i==10)BREAK; |
||
3263 | } |
||
3264 | Return to contents. |
||
7496 | leency | 3265 | |
3266 | |||
7544 | leency | 3267 | |
7496 | leency | 3268 | 9.9 Оператор продолжения continue, CONTINUE. |
7544 | leency | 3269 | |
7496 | leency | 3270 | |
3271 | Оператор продолжения continue передает управление на следующую |
||
3272 | итерацию в циклах do-while, for, while, loop, loopnz. В циклах |
||
3273 | do-while, while, loop следующая итерация начинается с вычисления |
||
3274 | условного выражения. Для цикла for следующая итерация начинается с |
||
3275 | вычисления выражения приращения, а затем происходит вычисление условного |
||
3276 | выражения. |
||
3277 | |||
3278 | Оператор CONTINUE аналогичен continue, но при этом генерируется код на |
||
3279 | 1 байт короче. Размер сгенерированного кода от места где применяется |
||
3280 | CONTINUE до начала итерации должен быть меньше 127 байт. |
||
7544 | leency | 3281 | Return to contents. |
7496 | leency | 3282 | |
3283 | |||
7544 | leency | 3284 | |
7496 | leency | 3285 | 9.10 Логическое объединение условий. |
7544 | leency | 3286 | |
7496 | leency | 3287 | |
3288 | Существует возможность логического объединения сравнений в условиях |
||
3289 | IF и if, циклах do{}while, while{}, WHILE{}, for{} и FOR{}. |
||
3290 | Каждое сравнение при их логическом объединении должно быть заключено в |
||
3291 | скобки. Объединять можно не более 32 сравнений. |
||
3292 | |||
3293 | В отличие от C в C-- анализ логических объединений происходит слева |
||
3294 | направо и все лишние скобки будут восприняты компилятором как ошибочные. |
||
3295 | Это несколько снижает гибкость и возможности применения этих объединений, |
||
3296 | но такова традиция и философия, заложенная в C--. |
||
3297 | |||
3298 | Пример: |
||
3299 | |||
7544 | leency | 3300 | if ( (a>3) && (b>4) || (c<8) ){ |
7496 | leency | 3301 | |
3302 | Т.е. если произвести расшифровку этого условия, то получится следующее: |
||
3303 | условие выполнится если a>3 и b>4 или a>3 и c<8. |
||
7544 | leency | 3304 | Return to contents. |
7496 | leency | 3305 | |
3306 | |||
7544 | leency | 3307 | |
7496 | leency | 3308 | 9.11 Переход через циклы. |
7544 | leency | 3309 | |
7496 | leency | 3310 | |
3311 | Для операторов BREAK, break, CONTINUE, continue введена |
||
3312 | поддержка числового параметра, определяющего, сколько циклов надо |
||
3313 | пропустить, прежде чем будет выполнен этот оператор. Например, мы имеем три |
||
3314 | вложенных цикла: |
||
3315 | |||
3316 | do{ |
||
3317 | loop(CX){ |
||
3318 | for(BX=0;BX<10;BX++){ |
||
7544 | leency | 3319 | break; //стандартный оператор |
3320 | break 0; //break с параметром - пропустить 0 циклов |
||
3321 | break 1; //break с параметром - пропустить 1 цикл |
||
3322 | break 2; //break с параметром - пропустить 2 цикла |
||
7496 | leency | 3323 | } |
3324 | LABL0: |
||
3325 | } |
||
3326 | LABL1: |
||
3327 | }while (DX!=0); |
||
3328 | LABL2: |
||
3329 | |||
3330 | В третьем цикле находится группа различных вариантов оператора break. |
||
3331 | Первым стоит стандартный оператор break, при выполнении которого |
||
3332 | управление будет передаваться за пределы третьего цикла - на метку LABL0. |
||
3333 | Вторым идет оператор break 0, при выполнении которого будет пропущено 0 |
||
3334 | циклов и управление будет передано опять же на метку LABL0. Таким |
||
3335 | образом, запись break и break 0 являются синонимами. Третьим идет |
||
3336 | оператор break 1, при выполнении которого будет пропущен один цикл и |
||
3337 | управление будет передано за пределы второго цикла на метку LABL1. Ну и |
||
3338 | наконец, последним идет оператор break 2, при выполнении которого |
||
3339 | компилятор пропустит два цикла и передаст управление за пределы третьего, |
||
3340 | на метку LABL2. Метки в этом примере проставлены для удобства объяснения. |
||
3341 | Ну и я надеюсь, Вам понятно, что значение параметра не может превышать |
||
3342 | числа циклов находящихся перед текущим. Так для одиночного цикла этот |
||
3343 | параметр может принимать максимальное и единственное значение - 0. |
||
7544 | leency | 3344 | Return to contents. |
7496 | leency | 3345 | |
3346 | |||
7544 | leency | 3347 | |
7496 | leency | 3348 | 9.12 Инвертирование флага проверки условий. |
7544 | leency | 3349 | |
7496 | leency | 3350 | |
3351 | Инвертирование флага проверки условий в операциях сравнения if/IF |
||
3352 | for/FOR while/WHILE происходит с помощью символа ! - not. |
||
3353 | |||
3354 | Выражени |
||
3355 | |||
3356 | IF ( NOTCARRYFLAG )... и IF ( ! CARRYFLAG )... |
||
3357 | IF ( proc() == 0 )... и IF ( ! proc() ) ... |
||
3358 | |||
3359 | являются синонимами. |
||
7544 | leency | 3360 | Return to contents. |
7496 | leency | 3361 | |
3362 | |||
7544 | leency | 3363 | |
7496 | leency | 3364 | 9.13 Вычисление выражения, а затем проверка условия. |
7544 | leency | 3365 | |
7496 | leency | 3366 | |
3367 | В операциях сравнения в левом операнде теперь допустимо использовать |
||
3368 | вычисления выражения с присваиванием и операции инкремента, декремента. |
||
3369 | Например: |
||
3370 | |||
3371 | IF (i=a+2 != 0 )... |
||
3372 | IF ( i++ )... |
||
3373 | IF ( a-- )... |
||
3374 | IF ( i+=4 == 0 )... |
||
3375 | |||
3376 | Во всех этих примерах сначала произойдет вычисление выражения в левой |
||
3377 | части операции сравнения, а потом будет произведено сравнение результата с |
||
3378 | правой частью выражения сравнения. |
||
7544 | leency | 3379 | Return to contents. |
7496 | leency | 3380 | |
3381 | |||
7544 | leency | 3382 | |
7496 | leency | 3383 | 9.14 Проверка битов при операции сравнения. |
7544 | leency | 3384 | |
7496 | leency | 3385 | |
3386 | Если в левой части выражения сравнения написано: BX & 5, то при |
||
3387 | вычислении выражения содержимое регистра BX будет изменено инструкцией |
||
3388 | and. Но иногда возникает необходимость в проверке битов без изменения |
||
3389 | содержимого регистра BX. Для этих целей надо использовать инструкцию |
||
3390 | test. Как же указать компилятору, в каких ситуациях использовать |
||
3391 | инструкцию and, а в каких test? В стандартных языках C для этого |
||
3392 | используется механизм приоритетов - если выражение заключено в скобки, то |
||
3393 | производится его вычисление, если нет, то производится проверка. Но C-- не |
||
3394 | поддерживает приоритетов. Для разрешения этой проблемы в C-- решено |
||
3395 | использовать непосредственно саму инструкцию test. Вот допустимые |
||
3396 | варианты синтаксиса: |
||
3397 | |||
3398 | IF ( $test AX,5 ) |
||
3399 | IF ( ! $test AX,5) |
||
3400 | IF ( asm test AX,5) |
||
3401 | IF ( ! asm { test AX,5 } ) |
||
7544 | leency | 3402 | Return to contents. |
7496 | leency | 3403 | |
3404 | |||
7544 | leency | 3405 | |
7496 | leency | 3406 | 9.15 Оператор перестановки. |
7544 | leency | 3407 | |
7496 | leency | 3408 | |
3409 | В C-- есть оператор, который не встречается в других языках, это |
||
3410 | оператор перестановки. Оператор перестановки меняет местами содержимое двух |
||
3411 | переменных. Символьное обозначение этого оператора ><. Переменные с обеих |
||
3412 | сторон оператора перестановки должны иметь одинаковый размер, 8 бит и 8 |
||
3413 | бит, 16 бит и 16 бит, или 32 бита и 32 бита. |
||
3414 | |||
3415 | Вот некоторые примеры: |
||
3416 | |||
3417 | AX >< BX; // сохраняет значение BX в AX и значение AX в BX |
||
3418 | CH >< BL; // меняет местами содержимое регистров CH и BL |
||
3419 | dog >< cat; /* меняет местами значения переменной dog и переменной cat*/ |
||
3420 | counter >< CX; // меняет местами значения переменной counter |
||
3421 | // и содержимое регистра CX |
||
3422 | |||
3423 | Если перестановка осуществляется между двумя 8-разрядными переменными в |
||
3424 | памяти, будет разрушено содержимое регистра AL. Если перестановка - между |
||
3425 | двумя 16-разрядными переменными в памяти, будет разрушено содержимое |
||
3426 | регистра AX. Если перестановка - между двумя 32-разрядными переменными в |
||
3427 | памяти, будет разрушено содержимое EAX. В любом другом случае, например, |
||
3428 | между переменной в памяти и регистром, значения всех регистров будут |
||
3429 | сохранены. |
||
7544 | leency | 3430 | Return to contents. |
7496 | leency | 3431 | |
3432 | |||
7544 | leency | 3433 | |
7496 | leency | 3434 | 9.16 Оператор отрицания. |
7544 | leency | 3435 | |
7496 | leency | 3436 | |
3437 | C-- поддерживает быстрый синтаксис смены знака переменной - оператор |
||
3438 | отрицания. Поставив - (знак минус) перед идентификатором переменной памяти |
||
3439 | или регистра и ; (точку с запятой) после идентификатора, вы смените знак |
||
3440 | переменной памяти или регистра. |
||
3441 | |||
3442 | Вот некоторые примеры: |
||
3443 | |||
3444 | -AX; // результат тот же, что и при 'AX = -AX;' ,но быстрее. |
||
3445 | -tree; // то же самое, что 'tree = -tree;' ,но быстрее. |
||
3446 | -BH; // меняет знак BH. |
||
7544 | leency | 3447 | Return to contents. |
7496 | leency | 3448 | |
3449 | |||
7544 | leency | 3450 | |
7496 | leency | 3451 | 9.17 Оператор инверсии. |
7544 | leency | 3452 | |
7496 | leency | 3453 | |
3454 | C-- поддерживает быстрый синтаксис выполнения логической инверсии |
||
3455 | значения переменной - оператор инверсии. Поставив ! (восклицательный знак) |
||
3456 | перед идентификатором переменной памяти или регистром и ; (точку с |
||
3457 | запятой) после идентификатора, вы выполните логическую (выполнится |
||
3458 | ассемблерная команда NOT) инверсию текущего значения переменной. Вот |
||
3459 | некоторые примеры: |
||
3460 | |||
3461 | !AX; // то же самое, что ' AX ^ = 0xFFFF; ' но быстрее. |
||
3462 | !node; // заменяет значение 'node' его логической инверсией. |
||
3463 | !CL; // то же самое, что ' CL ^ = 0xFF ' но быстрее. |
||
7544 | leency | 3464 | Return to contents. |
7496 | leency | 3465 | |
3466 | |||
7544 | leency | 3467 | |
7496 | leency | 3468 | 9.18 Специальные условные выражения. |
7544 | leency | 3469 | |
7496 | leency | 3470 | |
3471 | C-- поддерживает восемь специальных условных выражений: |
||
3472 | |||
3473 | CARRYFLAG |
||
3474 | NOTCARRYFLAG |
||
3475 | OVERFLOW |
||
3476 | NOTOVERFLOW |
||
3477 | ZEROFLAG |
||
3478 | NOTZEROFLAG |
||
3479 | MINUSFLAG |
||
3480 | PLUSFLAG |
||
3481 | |||
3482 | Они могут использоваться вместо любых нормальных условных выражений. |
||
3483 | Если Вы желаете, например, выполнить блок кода только если установлен флаг |
||
3484 | переноса, Вам следует использовать следующую последовательность команд: |
||
3485 | |||
3486 | IF( CARRYFLAG ) |
||
3487 | { |
||
3488 | // здесь вы чего-то делаете |
||
3489 | } |
||
3490 | |||
3491 | Если Вы желаете непрерывно выполнять блок кода до тех пор, пока не |
||
3492 | установится флаг переполнения, Вам следует использовать нечто подобное |
||
3493 | следующему куску кода: |
||
3494 | |||
3495 | do { |
||
3496 | // здесь вы опять чего-то делаете |
||
3497 | } while( NOTOVERFLOW ); |
||
7544 | leency | 3498 | Return to contents. |
7496 | leency | 3499 | |
3500 | |||
7544 | leency | 3501 | |
7496 | leency | 3502 | 9.19 Символ $ - вставляет текущий адрес программы. |
7544 | leency | 3503 | |
7496 | leency | 3504 | |
3505 | Символ $, кроме того, что является признаком последующей ассемблерной |
||
3506 | инструкции, в языке C--, как и в языке Assembler может указывать текущий |
||
3507 | адрес (смещение) компилируемой программы. Но в C-- он имел ограниченные |
||
3508 | возможности. Он мог быть использован лишь как аргумент в операторах |
||
3509 | GOTO/goto и ассемблерных инструкциях DW/DD/JMP. |
||
3510 | |||
3511 | Этот символ может находиться в любом месте вычисляемого числового |
||
3512 | выражения и может быть применен в любом месте совместно с другими числовыми |
||
3513 | выражениями. |
||
3514 | |||
3515 | Примеры применения: |
||
3516 | |||
7544 | leency | 3517 | DW #main-$ //записать расстояние от процедуры main до текущего места |
3518 | GOTO $+2; //перейти по адресу на 2 больше, чем текущий адрес |
||
3519 | Return to contents. |
||
7496 | leency | 3520 | |
3521 | |||
7544 | leency | 3522 | |
7496 | leency | 3523 | 9.20 Ключевое слово static и оператор ::. |
7544 | leency | 3524 | |
7496 | leency | 3525 | |
3526 | Если перед объявлением глобальной переменной, структуры или процедуры |
||
3527 | указать слово static, то эти переменная, структура или процедура будут |
||
3528 | доступны только в том файле, в котором они были объявлены. Т.е. если Вы |
||
3529 | включите этот файл в другой директивой include, то переменные объявленные |
||
3530 | во включаемом файле со словом static не будут доступны в основном файле, |
||
3531 | и Вы можете в основном файле объявить другие переменные с такими же |
||
3532 | именами. |
||
3533 | |||
3534 | Если Вы примените слово static при объявлении локальной переменной в |
||
3535 | процедуре, то память для этой переменной будет выделена не в стеке, а в |
||
3536 | области данных процедуры. Но эта переменная будет доступна только внутри |
||
3537 | процедуры, в которой она была объявлена. Применение static к локальным |
||
3538 | переменным дает возможность сохранять значение переменной для следующего |
||
3539 | входа в процедуру. |
||
3540 | |||
3541 | Слово static можно применять к любому глобальному объекту |
||
3542 | (переменной, структуре, процедуре). Для локального использования это слово |
||
3543 | можно применять только к переменным. |
||
3544 | |||
3545 | Если в Вашей программе есть глобальная и локальная переменная с |
||
3546 | одинаковыми именами, то в процедуре, в которой объявлена эта локальная |
||
3547 | переменная, Вы не имели доступа к одноименной глобальной переменной. |
||
3548 | Применив перед именем переменной оператор ::, Вы получите доступ к |
||
3549 | глобальной переменной. Пример: |
||
3550 | |||
3551 | int var; //объявляем глобальную переменную |
||
3552 | |||
3553 | void proc() |
||
3554 | int var; //объявляем локальную переменную с именем уже существующей |
||
3555 | //глобальной переменной |
||
3556 | { |
||
7544 | leency | 3557 | (E)AX=var; //имеем доступ только к локальной переменной |
7496 | leency | 3558 | (E)AX=::var; //а так можно получить доступ к глобальной переменной |
3559 | } |
||
7544 | leency | 3560 | Return to contents. |
7496 | leency | 3561 | |
3562 | |||
7544 | leency | 3563 | |
7496 | leency | 3564 | 9.21 Оператор sizeof. |
7544 | leency | 3565 | |
7496 | leency | 3566 | |
3567 | Операция sizeof определяет размер памяти, который соответствует объекту |
||
3568 | или типу. Операция sizeof имеет следующий вид: |
||
3569 | |||
3570 | sizeof (<имя типа>) |
||
3571 | |||
3572 | Результатом операции sizeof является размер памяти в байтах, |
||
3573 | соответствующий заданному объекту или типу. |
||
3574 | |||
3575 | В C-- оператор sizeof можно применять к переменным, регистрам, типам |
||
3576 | переменных, структурам, процедурам, текстовым строкам и файлам. |
||
3577 | |||
3578 | Если операция sizeof применяется к типу структуры, то результатом |
||
3579 | является размер тега данной структуры. |
||
3580 | |||
3581 | Если операция sizeof применяется к текстовой строке, то результатом |
||
3582 | операции является размер строки плюс завершающий нуль. Например: |
||
3583 | |||
3584 | sizeof ("Test") |
||
3585 | |||
3586 | результатом этой операции будет число 5. Если Вы напишите такую |
||
3587 | конструкцию: |
||
3588 | |||
3589 | char a="Test"; |
||
3590 | |||
3591 | sizeof(a) |
||
3592 | |||
3593 | то результатом будет 5 - размер памяти, отведенный для переменной a. |
||
3594 | |||
3595 | При использовании оператора sizeof с именем структуры вставляет |
||
3596 | фактический размер памяти, занимаемый структурой. Это особенно важно, если |
||
3597 | Вы объявили массив структур. |
||
3598 | |||
3599 | Оператор sizeof можно применять и к имени определенной ранее |
||
3600 | процедуры. Результатом будет размер этой процедуры. Но для динамических |
||
3601 | процедур всегда будет ноль. |
||
3602 | |||
3603 | Операцию sizeof можно применять и к файлам. Это бывает очень полезным |
||
3604 | при использовании оператора FROM, но может применяться и в других случаях. |
||
3605 | Пример применения оператора sizeof к файлам: |
||
3606 | |||
3607 | sizeof ( file "filename.dat" ) |
||
3608 | |||
3609 | Результатом этой операции будет размер файла "filename.dat". |
||
7544 | leency | 3610 | Return to contents. |
7496 | leency | 3611 | |
3612 | |||
7544 | leency | 3613 | |
7496 | leency | 3614 | 9.22 Метки перехода. |
7544 | leency | 3615 | |
7496 | leency | 3616 | |
3617 | Метки перехода применяются для указания начальных точек участков кода, |
||
3618 | используемых командами перехода встроенного ассемблера и операторами |
||
3619 | goto/GOTO. |
||
3620 | |||
3621 | Имеются два типа меток перехода: глобальные и локальные. Глобальные |
||
3622 | метки, как следует из названия, это метки, которые видимы из любого места в |
||
3623 | программе. Локальные метки видны только в пределах своего процедурного |
||
3624 | блока, и не определены за его пределами. |
||
3625 | |||
3626 | Метки определяются идентификатором, оканчивающимися двоеточием. Если |
||
3627 | идентификатор содержит хотя бы один символ строчных букв (букв нижнего |
||
3628 | регистра, маленьких букв), это глобальная метка перехода, в противном |
||
3629 | случае, это локальная метка перехода. |
||
3630 | |||
3631 | Глобальные метки перехода не должны использоваться внутри динамических |
||
3632 | процедур; там можно использовать только локальные метки. Это важно помнить, |
||
3633 | поскольку, из-за применения такого средства как макрокоманды, динамическая |
||
3634 | процедура может присутствовать в нескольких местах кода, что будет |
||
3635 | означать, что метке соответствует больше чем один адрес. |
||
3636 | |||
3637 | Метки вне процедур фактически располагаются в области данных программы. |
||
3638 | Если данные и код находятся в одном сегменте (а именно так организованна |
||
3639 | программа, написанная на C--), то метки вне процедур становятся простым и |
||
3640 | эффективным методом для получения расстояний между частями программы. В |
||
3641 | качестве имен для меток вне процедур могут быть использованы уникальные |
||
3642 | идентификаторы, в которых можно использовать большие, маленькие и смесь |
||
3643 | больших и маленьких букв. |
||
7544 | leency | 3644 | Return to contents. |
7496 | leency | 3645 | |
3646 | |||
7544 | leency | 3647 | |
7496 | leency | 3648 | 10. Ассемблер. |
3649 | |||
3650 | 10.1 Поддержка команд ассемблера. |
||
7544 | leency | 3651 | |
7496 | leency | 3652 | |
3653 | Встроенный в C-- ассемблер поддерживает все инструкции 8088/8086, |
||
3654 | 80286, 80386, 80486, Pentium, Pentium II и Pentium III процессоров. |
||
3655 | |||
3656 | Все инструкции встроенного ассемблера должны начинаться с символа |
||
3657 | доллара $. Поддерживается также ключевое слово asm, которое являясь |
||
3658 | синонимом к символу доллара, еще и поддерживает объединение ассемблерных |
||
3659 | инструкций в блоки. |
||
7544 | leency | 3660 | Return to contents. |
7496 | leency | 3661 | |
3662 | |||
7544 | leency | 3663 | |
7496 | leency | 3664 | 10.2 Ключевое слово asm. |
7544 | leency | 3665 | |
7496 | leency | 3666 | |
3667 | Ключевое слово asm является синонимом к $ - префикс ассемблерной |
||
3668 | команды. После слова asm можно писать блок ассемблерных команд. Пример: |
||
3669 | |||
7544 | leency | 3670 | asm { |
3671 | . |
||
3672 | . |
||
3673 | push AX |
||
7496 | leency | 3674 | labl: |
7544 | leency | 3675 | push BX |
3676 | mov AX,0x1234 |
||
3677 | jmp short labl |
||
3678 | . |
||
3679 | . |
||
3680 | . |
||
3681 | } |
||
7496 | leency | 3682 | |
3683 | Метки внутри блока ассемблерных команд допустимы. |
||
7544 | leency | 3684 | Return to contents. |
7496 | leency | 3685 | |
3686 | |||
7544 | leency | 3687 | |
7496 | leency | 3688 | 10.3 Префикс dup - повторение инструкций DB/DW/DD. |
7544 | leency | 3689 | |
7496 | leency | 3690 | |
3691 | Для ассемблерных инструкции DB, DW, DD введена возможность использовать |
||
3692 | префикс повторений dup. Применение этого префикса имеет следующий |
||
3693 | синтаксис: |
||
3694 | |||
3695 | $DW NUMREP dup VALTOREP |
||
3696 | |||
3697 | NUMREP - число повторов инструкции DW. |
||
3698 | VALTOREP - величина, которая будет повторена NUMREP раз. |
||
3699 | |||
3700 | В отличие от аналога этого префикса из ассемблера повторяемую величину |
||
3701 | заключать в скобки нельзя. |
||
7544 | leency | 3702 | Return to contents. |
7496 | leency | 3703 | |
3704 | |||
7544 | leency | 3705 | |
7496 | leency | 3706 | 10.4 Инструкции процессора Pentium III. |
7544 | leency | 3707 | |
7496 | leency | 3708 | |
3709 | В компилятор добавлена поддержка 19 новых инструкций MMX расширения |
||
3710 | |||
3711 | MASKMOVQ mmx,mmx |
||
3712 | MOVNTQ m64,mmx |
||
3713 | PAVGB mmx,mmx/m64 |
||
3714 | PAVGW mmx,mmx/m64 |
||
3715 | PEXTRW r32,mmx,i8 |
||
3716 | PINSRW mmx,r32/m16,i8 |
||
3717 | PMAXUB mmx,mmx/m64 |
||
3718 | PMAXSW mmx,mmx/m64 |
||
3719 | PMINUB mmx,mmx/m64 |
||
3720 | PMINSW mmx,mmx/m64 |
||
3721 | PMOVMSKB r32,mmx |
||
3722 | PMULHUW mmx,mmx/m64 |
||
3723 | PREFETCHT0 mem |
||
3724 | PREFETCHT1 mem |
||
3725 | PREFETCHT2 mem |
||
3726 | PREFETCHNTA mem |
||
3727 | SFENCE |
||
3728 | PSADBW mmx,mmx/m64 |
||
3729 | PSHUFW mmx,mmx/m64,i8 |
||
3730 | |||
3731 | и 46 инструкций SSE расширения. |
||
3732 | |||
3733 | ADDPS xmm,m128/xmm |
||
3734 | ADDSS xmm,xmm/m32 |
||
3735 | ANDNPS xmm,xmm/m128 |
||
3736 | ANDPS xmm,xmm/m128 |
||
3737 | COMISS xmm,xmm/m32 |
||
3738 | DIVPS xmm,m128/xmm |
||
3739 | DIVSS xmm,xmm/m32 |
||
3740 | MAXPS xmm,m128/xmm |
||
3741 | MAXSS xmm,xmm/m32 |
||
3742 | MINPS xmm,m128/xmm |
||
3743 | MINSS xmm,xmm/m32 |
||
3744 | MULPS xmm,m128/xmm |
||
3745 | MULSS xmm,xmm/m32 |
||
3746 | ORPS xmm,xmm/m128 |
||
3747 | RCPPS xmm,xmm/m128 |
||
3748 | RCPSS xmm,xmm/m32 |
||
3749 | RSQRTPS xmm,xmm/m128 |
||
3750 | RSQRTSS xmm,xmm/m32 |
||
3751 | SQRTPS xmm,m128/xmm |
||
3752 | SQRTSS xmm,xmm/m32 |
||
3753 | SUBPS xmm,m128/xmm |
||
3754 | SUBSS xmm,xmm/m32 |
||
3755 | UCOMISS xmm,xmm/m32 |
||
3756 | UNPCKHPS xmm,xmm/m128 |
||
3757 | UNPCKLPS xmm,xmm/m128 |
||
3758 | XORPS xmm,xmm/m128 |
||
3759 | CMPPS xmm,xmm/m128,i8 |
||
3760 | CMPSS xmm,xmm/m32,i8 |
||
3761 | SHUFPS xmm,xmm/m128,i8 |
||
3762 | CVTPI2PS xmm,m64/mmx |
||
3763 | CVTSI2SS xmm,m32/r32 |
||
3764 | CVTPS2PI mmx,m128/xmm |
||
3765 | CVTTPS2PI mmx,xmm/m128 |
||
3766 | CVTSS2SI r32,xmm/m128 |
||
3767 | CVTTSS2SI r32,xmm/m128 |
||
3768 | LDMXCSR m32 |
||
3769 | STMXCSR m32 |
||
3770 | MOVHLPS xmm,xmm |
||
3771 | MOVLHPS xmm,xmm |
||
3772 | MOVMSKPS r32,xmm |
||
3773 | MOVNTPS m128,xmm |
||
3774 | MOVAPS m128/xmm,xmm/m128 |
||
3775 | MOVSS xmm/m32,xmm/m32 |
||
3776 | MOVUPS xmm/m128,m128/xmm |
||
3777 | MOVHPS xmm/m64,m64/xmm |
||
3778 | MOVLPS xmm/m64,m64/xmm |
||
3779 | |||
3780 | Многие из этих инструкций могут использовать в качестве операнда |
||
3781 | 64-битные и 128-битные ячейки памяти. Компилятор C-- сейчас может работать |
||
3782 | только с 32-битными переменными. Поэтому для инструкций использующих в |
||
3783 | качестве операнда ячейки памяти размером больше 32-бит можно использовать |
||
3784 | переменные любых типов. Компилятор не будет выдавать на это сообщений об |
||
3785 | ошибке, будет использован адрес этой переменной, а сама инструкция будет |
||
3786 | использовать нужное ей число битов памяти, начиная с адреса указанной |
||
3787 | переменной. Например: |
||
3788 | |||
3789 | Для инструкции movaps один из операндов может быть 128-битной |
||
3790 | ячейкой памяти. Для этой инструкции допустимы следующий синтаксис: |
||
3791 | |||
3792 | byte var8_128[16]; |
||
3793 | word var16_128[8]; |
||
3794 | dword var32_128[4]; |
||
3795 | |||
3796 | void proc() |
||
3797 | { |
||
3798 | asm{ |
||
3799 | movaps var8_128,xmm0 //в массив из 16 байт будет записано содержимое XMM0 |
||
7544 | leency | 3800 | movaps xmm1,var16_128 //в XMM1 будет записано содержимое 8 слов |
7496 | leency | 3801 | movaps var32_128,xmm1 //в массив из 4 двойных слов будет записано XMM1 |
3802 | } |
||
3803 | } |
||
7544 | leency | 3804 | Return to contents. |
7496 | leency | 3805 | |
3806 | |||
7544 | leency | 3807 | |
7496 | leency | 3808 | 11. Процедуры. |
3809 | |||
3810 | 11.1 Типы процедур, функций и макрокоманд. |
||
7544 | leency | 3811 | |
7496 | leency | 3812 | |
3813 | Сейчас C-- поддерживает 4 типа вызова процедур: cdecl, pascal, stdcall |
||
3814 | и fastcall. Вот краткие характеристики этих типов вызовов процедур: |
||
3815 | |||
3816 | cdecl Этот тип вызова процедур является по умолчанию для языка С. Он |
||
3817 | характеризуется тем, что параметры процедуры передаются в порядке обратном |
||
3818 | их записи. Очистка стека от параметров производится после завершения работы |
||
3819 | процедуры. Этот способ вызова процедур очень удобен для процедур с |
||
3820 | переменным числом параметров. |
||
3821 | |||
3822 | pascal Этот тип вызова предполагает, что параметры передаются в том |
||
3823 | порядке, в котором они записаны в программе. Освобождение стека от |
||
3824 | параметров производит сама вызываемая процедура. Этот тип вызова является |
||
3825 | более компактным, чем cdecl. |
||
3826 | |||
3827 | stdcall Этот тип вызова является гибридом первых двух. Параметры |
||
3828 | передаются процедуре в порядке обратном, тому в котором они записаны в |
||
3829 | программе. Освобождение стека от параметров производится в самой вызываемой |
||
3830 | процедуре. |
||
3831 | |||
3832 | fastcall Этот тип вызова процедур предполагает что передача параметров |
||
3833 | процедуре производится через регистры, тем самым отпадает необходимость |
||
3834 | освобождения стека от параметров. Для этого типа вызова процедуры |
||
3835 | существуют ограничения по числу передаваемых параметров. Для C это три |
||
3836 | параметра, а для C-- шесть. В C-- параметры передаются по умолчанию в |
||
3837 | следующем порядке: 1-й - AX/EAX, 2-й - BX/EBX, 3 - CX/ECX, 4 - DX/EDX, 5 - |
||
3838 | DI/EDI, 6 - SI/ESI. Параметры типов char или byte могут передаваться в |
||
3839 | количестве не более 4 или только в первых 4 регистрах: 1 - AL, 2 - BL, 3 - |
||
3840 | CL, 4 - DL. Этот порядок регистров может быть изменен, если явно указать |
||
3841 | его либо при объявлении процедуры, либо при ее определении. Процедуры типа |
||
3842 | fastcall иногда еще называют регистровыми. |
||
3843 | |||
3844 | В C-- по умолчанию, если имя процедуры написано большими буквами, то |
||
3845 | считается, что эта процедура имеет тип вызова fastcall. Если же в имени |
||
3846 | процедуры есть хотя бы одна маленькая буква, то по умолчанию считается, что |
||
3847 | эта процедура имеет тип вызова pascal, за исключением программ |
||
3848 | компилируемых с ключом /w32 /w32c или /DLL. В них по умолчанию применяется |
||
3849 | тип вызова процедур stdcall. Если же Вы хотите изменить тип вызова процедур |
||
3850 | из по умолчанию на любой другой, то эту процедуру надо обязательно объявить |
||
3851 | с указанием типа желаемого вызова. |
||
3852 | |||
3853 | Объявление процедур введено для того, чтобы сообщать компилятору о |
||
3854 | типе возврата из процедур, способе передачи параметров процедуре и их числе. |
||
7544 | leency | 3855 | Return to contents. |
7496 | leency | 3856 | |
3857 | |||
7544 | leency | 3858 | |
7496 | leency | 3859 | 11.2 Стековые процедуры. |
7544 | leency | 3860 | |
7496 | leency | 3861 | |
3862 | Стековые процедуры по умолчанию объявляются при помощи идентификатора, |
||
3863 | который содержит, по крайней мере, один символ строчных букв (букв нижнего |
||
3864 | регистра, маленьких букв). Таким образом, стековые процедуры легко отличимы |
||
3865 | от регистровых процедур, поскольку для имен регистровых процедур символы |
||
3866 | строчных букв запрещены. |
||
3867 | |||
3868 | Параметры для стековых процедур, если они есть, могут иметь любой тип |
||
3869 | byte, char, word, int, dword, long или float. |
||
3870 | |||
3871 | Параметры передаются в соответствии с правилами, принятыми для данного |
||
3872 | типа процедур. Если процедура не имеет объявления, то компилятор не следит |
||
3873 | за числом и типом передаваемых параметров. В этом случае у Вас появляется |
||
3874 | свобода в их использовании, но Вы должны осознавать и последстви |
||
3875 | неправильного их использования. |
||
3876 | |||
3877 | В списке параметров для каждого параметра указывается его тип. |
||
3878 | Параметры одного типа, идущие подряд, разделяются запятыми. Формальные |
||
3879 | параметры разного типа в объявлении функции разделяются символом ;. |
||
3880 | |||
3881 | В следующем примере стековая процедура возвращает сумму всех своих |
||
3882 | параметров (имеющих различные типы) как величину типа word: |
||
3883 | |||
7544 | leency | 3884 | word add_them_all (int a,b,c; byte d,e; word x,y) |
3885 | { |
||
3886 | return( a+b+c+d+e+x+y ); |
||
3887 | } |
||
7496 | leency | 3888 | |
3889 | Ранее C-- делал вызовы стековых процедур лишь в стиле pascal. |
||
3890 | Преимуществом этого способа вызова процедур является компактность и более |
||
3891 | простой механизм генерации кода. К недостаткам, а соответственно и |
||
3892 | преимуществам С-стиля, можно отнести жесткую привязанность паскалевских |
||
3893 | процедур к числу и типу передаваемых параметров (попробуйте при вызове |
||
3894 | процедуры в стиле pascal опустить один параметр и получите 100% зависание). |
||
3895 | Напомню некоторые технические детали обоих типов вызовов процедур. |
||
3896 | |||
3897 | Кадр стека C-- для близких процедур стека в стиле pascal: |
||
3898 | АДРЕС |
||
3899 | ... |
||
3900 | BP + FFFE предпоследний байта локальных переменных |
||
3901 | BP + FFFF последний байт локальных переменных |
||
3902 | BP + 0000 Сохраненный BP |
||
3903 | BP + 0002 RET адрес |
||
3904 | BP + 0004 последнее слово передаваемых процедуре параметров (если они |
||
3905 | есть) |
||
3906 | BP + 0006 предпоследнее слово передаваемых процедуре параметров |
||
3907 | ... |
||
3908 | BP + nnnn первое слово передаваемых процедуре параметров |
||
3909 | |||
3910 | Освобождение стека от переданных процедуре параметров происходит прямо |
||
3911 | в самой процедуре командой RET nnnn - где nnnn является размером переданных |
||
3912 | в стек параметров. |
||
3913 | |||
3914 | Кадр стека C-- для близких процедур стека в стиле си: |
||
3915 | АДРЕС |
||
3916 | ... |
||
3917 | BP + FFFE предпоследний байта локальных переменных |
||
3918 | BP + FFFF последний байт локальных переменных |
||
3919 | BP + 0000 Сохраненный BP |
||
3920 | BP + 0002 RET адрес |
||
3921 | BP + 0004 первое слово передаваемых процедуре параметров (если они |
||
3922 | есть) |
||
3923 | BP + 0006 второе слово передаваемых процедуре параметров |
||
3924 | ... |
||
3925 | BP + nnnn последнее слово передаваемых процедуре параметров |
||
3926 | |||
3927 | Процедуры в стиле С заканчиваются командой RET. Освобождение стека от |
||
3928 | параметров происходит в том месте откуда была вызвана процедура. Обычно это |
||
3929 | делается командой ADD SP,nnnn. Т.е. компилятор может точно знать сколько и |
||
3930 | каких параметров Вы передаете в данном случае процедуре и соответственно |
||
3931 | освобождает стек после завершения процедуры. Это очень удобно для процедур, |
||
3932 | которые могут обрабатывать переменное число параметров (например, процедуры |
||
3933 | типа printf). |
||
3934 | |||
3935 | Объявление процедуры имеет следующий вид: |
||
3936 | |||
3937 | rettype modif procname(); |
||
3938 | |||
3939 | Первым идет необязательный тип возврата из процедур. По умолчанию он |
||
3940 | для 16-битных программ равен word, а для 32-битных dword. Затем должен идти |
||
3941 | также необязательный модификатор. По умолчанию все стековые процедуры в C-- |
||
3942 | (за исключением режима компиляции программ под Windows, где по умолчанию |
||
3943 | действует стиль вызова процедур stdcall) имеют стиль pascal. Далее идет им |
||
3944 | процедуры со скобками, которые являются признаком того что Вы объявляете |
||
3945 | процедуру, а не переменную. Завершает объявление символ точка с запятой. |
||
3946 | |||
3947 | При объявлении процедур в C-- прописывать параметры процедуры |
||
3948 | необязательно (тогда компилятор не будет контролировать число и тип |
||
3949 | передаваемых параметров), но если Вы их вставите, то включится механизм |
||
3950 | контроля за числом и типом параметров. |
||
7544 | leency | 3951 | Return to contents. |
7496 | leency | 3952 | |
3953 | |||
7544 | leency | 3954 | |
7496 | leency | 3955 | 11.3 Регистровые процедуры. |
7544 | leency | 3956 | |
7496 | leency | 3957 | |
3958 | Регистровые процедуры определяются, по умолчанию, при помощи |
||
3959 | идентификатора, который не содержит символов строчных букв. Или же явным |
||
3960 | указанием что это регистровая процедура с помощью ключевого слова fastcall. |
||
3961 | |||
3962 | Как уже было сказано, параметры (если они есть) для регистровой |
||
3963 | процедуры передаются через регистры. Регистровые процедуры могут иметь не |
||
3964 | более 6 параметров. Если параметры имеют тип int или word, регистры по |
||
3965 | умолчанию используются в следующем порядке: AX, BX, CX, DX, DI, и SI. |
||
3966 | Первые четыре параметра могут также иметь тип char или byte, в этом случае |
||
3967 | задействуются регистры AL, BL, CL и DL соответственно. Любой из шести |
||
3968 | параметров может иметь тип long, dword или float, тогда для него |
||
3969 | используется регистр EAX, EBX, ECX, EDX, EDI, или ESI. |
||
3970 | |||
3971 | В следующем примере регистровая процедура с именем TOGETHER возвращает |
||
3972 | значение типа word как результат умножения первого параметра, имеющего тип |
||
3973 | word, на второй параметр того же типа: |
||
3974 | |||
3975 | word TOGETHER() /* AX = первый параметр, BX = второй параметр */ |
||
3976 | { |
||
3977 | return (AX * BX); |
||
3978 | } |
||
3979 | |||
3980 | В следующем примере регистровая процедура с именем SHOW_NUM, которая не |
||
3981 | возвращает никакого значения, зато выводит на экран первый параметр |
||
3982 | (имеющий тип int), затем разделительный знак в виде двоеточия :, а затем |
||
3983 | второй параметр (имеющий тип byte) : |
||
3984 | |||
3985 | void SHOW_NUM () /* AX = первое число, BL = второе число */ |
||
3986 | { |
||
3987 | $ PUSH BX |
||
3988 | WRITEINT (int AX); |
||
3989 | WRITE (':'); |
||
3990 | $ POP BX |
||
3991 | WRITEWORD (BL); |
||
3992 | } |
||
3993 | |||
3994 | Но если в процедуре сделать объявление порядка и типов используемых |
||
3995 | регистров, то возможно произвольное использование регистров. Более подробно |
||
3996 | об этом можно почитать в разделе об объявлениях параметров в регистровых |
||
3997 | процедурах. |
||
3998 | |||
3999 | Для того, чтобы использовать регистровую процедуру как макрокоманду, |
||
4000 | она должна быть объявлена как динамическая процедура. Динамические |
||
4001 | процедуры описаны в следующем подразделе. |
||
7544 | leency | 4002 | Return to contents. |
7496 | leency | 4003 | |
4004 | |||
7544 | leency | 4005 | |
7496 | leency | 4006 | 11.4 Динамические процедуры. |
7544 | leency | 4007 | |
7496 | leency | 4008 | |
4009 | Динамические процедуры - процедуры, которые определены, но вставляются |
||
4010 | в код программы, только если есть вызов. Динамические процедуры могут |
||
4011 | использоваться как макрокоманды. |
||
4012 | |||
4013 | Определение динамической процедуры начинается с символа двоеточия ':'. |
||
4014 | |||
4015 | Пример динамической процедуры стека: |
||
4016 | |||
4017 | : void setvideomode (byte mode) |
||
4018 | { |
||
4019 | AL = mode; |
||
4020 | AH = 0; |
||
4021 | $ INT 0x10 |
||
4022 | } |
||
4023 | |||
4024 | Пример динамической регистровой процедуры: |
||
4025 | |||
4026 | : int ABS () /* AX = число, абсолютное значение которого ищется*/ |
||
4027 | { |
||
4028 | IF (int AX < 0) |
||
4029 | -AX; |
||
4030 | } |
||
7544 | leency | 4031 | Return to contents. |
7496 | leency | 4032 | |
4033 | |||
7544 | leency | 4034 | |
7496 | leency | 4035 | 11.4.1 Установка динамической процедуры в определенное место программы. |
7544 | leency | 4036 | |
7496 | leency | 4037 | |
4038 | Динамические процедуры, если они не используются как макросы и если |
||
4039 | они были востребованы в программе, вставляются в код программы в самом |
||
4040 | конце компиляции. В каком точно месте Вашей программы они окажутся узнать |
||
4041 | невозможно. Если же Вам необходимо, чтобы какая-то динамическая процедура |
||
4042 | находилась в конкретном месте программы, то это можно сделать таким |
||
4043 | образом: |
||
4044 | |||
4045 | :void proc ( int par1, par2) |
||
4046 | { |
||
4047 | ... |
||
4048 | } |
||
4049 | |||
4050 | Мы имеем динамическую процедуру, код которой был бы расположен ранее |
||
4051 | кода обычной процедуры нашей программы. Для этого перед определением этой |
||
4052 | процедуры надо написать такую строку: |
||
4053 | |||
4054 | @ void proc (); |
||
4055 | |||
7544 | leency | 4056 | В итоге динамическая процедура будет вставлена в код программы не в |
7496 | leency | 4057 | конце ее, как обычно, а в месте, где будет расположена эта строка. Если |
4058 | динамическая процедура имеет параметры, то прописывать эти параметры |
||
4059 | необязательно. |
||
4060 | |||
7544 | leency | 4061 | В компиляторе есть еще более мощное средство, позволяющее все |
7496 | leency | 4062 | динамические объекты ( процедуры, переменные, структуры ) расположить в |
4063 | указанном месте, а не в конце программы, как обычно. Это директива |
||
4064 | #setdinproc. Встретив эту директиву, компилятор немедленно расположит все |
||
4065 | известные ему на этот момент динамические объекты в месте объявления этой |
||
4066 | директивы. Последующие динамические объекты будут располагаться как |
||
4067 | обычно, в конце программы, если конечно, не будет повторно применена |
||
4068 | директива #setdinproc. |
||
4069 | |||
7544 | leency | 4070 | Это может быть применено и быть полезным при создании резидентных |
7496 | leency | 4071 | программ (TSR) и драйверов устройств. |
7544 | leency | 4072 | Return to contents. |
7496 | leency | 4073 | |
4074 | |||
7544 | leency | 4075 | |
7496 | leency | 4076 | 11.5 inline-процедуры. |
7544 | leency | 4077 | |
7496 | leency | 4078 | |
4079 | inline-процедурами могут быть динамические процедуры, которые можно |
||
4080 | использовать как макросы. Но в отличие от макросов, inline-процедуры, при |
||
4081 | включенной оптимизации на скорость, автоматически вставляются в код, а при |
||
4082 | оптимизации кода на размер, делается вызов их, как динамических процедур. |
||
4083 | |||
4084 | Но иногда бывает нужно при включенной оптимизации на размер кода, чтобы |
||
4085 | процедуры вставлялись в код, а не делался их вызов. Для этих целей введена |
||
4086 | директива #inline TRUE. Этой же директивой ( #inline FALSE ), можно при |
||
4087 | оптимизации на скорость делать вызовы процедур, вместо их вставки. |
||
4088 | |||
4089 | Важно помнить, что статус директивы #inline автоматически меняется при |
||
4090 | смене режима оптимизации. При установке оптимизации на скорость статус |
||
4091 | директивы #inline устанавливается в TRUE, а при смене режима оптимизации по |
||
4092 | размеру кода, устанавливается в FALSE. Поэтому применяйте директиву #inline |
||
4093 | лишь после смены режима оптимизации. |
||
4094 | |||
4095 | Директивы меняющие режим оптимизации #codesize, #speed и директива |
||
4096 | #inline, объявленные внутри процедуры распространяются только на оставшуюся |
||
4097 | часть процедуры, т.е. они становятся локальными. Для того чтобы изменения |
||
4098 | были глобальными эти директивы надо объявлять вне тела процедуры. |
||
4099 | |||
4100 | Для того чтобы определить inline-процедуру, надо в первой строке с |
||
4101 | именем процедуры вместо символа динамической процедуры (:) написать |
||
4102 | ключевое слово inline. Пример определения inline-процедуры: |
||
4103 | |||
4104 | inline int fastcall abs(AX) |
||
4105 | { |
||
4106 | IF ( int AX < 0 ) -AX ; |
||
4107 | } |
||
7544 | leency | 4108 | Return to contents. |
7496 | leency | 4109 | |
4110 | |||
7544 | leency | 4111 | |
7496 | leency | 4112 | 11.5.1 Другое применение inline. |
7544 | leency | 4113 | |
7496 | leency | 4114 | |
7544 | leency | 4115 | Ключевое слово inline имеет в процедурах и другое применение. Если |
7496 | leency | 4116 | это слово расположено перед началом блока процедуры, то для такой |
4117 | процедуры не создается кадр стека и не генерируется завершающий процедуру |
||
4118 | ret. Пример: |
||
4119 | |||
4120 | void PROC () |
||
4121 | inline |
||
4122 | { |
||
4123 | ... |
||
4124 | } |
||
4125 | |||
7544 | leency | 4126 | Такие процедуры не должны содержать локальных переменных. Если |
7496 | leency | 4127 | процедура является регистровой (тип fastcall), то с передачей ей |
4128 | параметров нет проблем. Если же процедура является стековой, то передать |
||
4129 | в такую процедуру параметры Вы можете, но воспользоваться этими |
||
4130 | параметрами используя их имена, Вы уже не сможете. Это происходит потому, |
||
4131 | что в этих процедурах кадр стека не формируется. Пример: |
||
4132 | |||
4133 | void proc (int par1, par2) |
||
4134 | inline |
||
4135 | { |
||
4136 | AX=par1; /* компилятор обратится с параметру 'par1' через регистр BP. |
||
7544 | leency | 4137 | Но так как кадр стека не был создан, при выполнении этого |
7496 | leency | 4138 | кода программа будет работать не правильно. */ |
4139 | ... |
||
4140 | } |
||
4141 | |||
7544 | leency | 4142 | Встретив такое определение процедуры, компилятор выдаст предупреждение |
7496 | leency | 4143 | о том, что в таких процедурах использовать локальные и параметрические |
4144 | переменные нельзя. |
||
7544 | leency | 4145 | Return to contents. |
7496 | leency | 4146 | |
4147 | |||
7544 | leency | 4148 | |
7496 | leency | 4149 | 11.6 Процедуры обработки прерываний. |
7544 | leency | 4150 | |
7496 | leency | 4151 | |
4152 | Процедуры обработки прерываний определяются следующим способом: |
||
4153 | |||
4154 | interrupt procedure_name () |
||
4155 | { |
||
4156 | // put code here (здесь должен быть код обработки) |
||
4157 | } |
||
4158 | |||
4159 | Процедуры обработки прерываний не сохраняют никаких регистров |
||
4160 | автоматически, и никакие регистры сами по себе не загружаются перед |
||
4161 | передачей управления обработчику прерывания, следовательно, на Вашей |
||
4162 | совести сохранение значений регистров в стеке и последующий их возврат, а |
||
4163 | также загрузка регистра DS нужным значением. |
||
4164 | |||
4165 | Вот пример обработчика прерывания, который сохраняет значения всех |
||
4166 | регистров и загружает регистр DS: |
||
4167 | |||
4168 | interrupt safe_handle () |
||
4169 | { |
||
4170 | $ PUSH DS |
||
4171 | $ PUSH ES |
||
4172 | $ PUSHA // для выполнения этой команды нужен процессор не хуже 80286 |
||
4173 | DS = CS; // здесь DS загружается для работы с моделью памяти типа tiny |
||
4174 | |||
4175 | |||
4176 | /* do your thing here (здесь вы делаете свою обработку)*/ |
||
4177 | |||
4178 | $ POPA // для выполнения этой команды нужен процессор не хуже 80286 |
||
4179 | $ POP ES |
||
4180 | $ POP DS |
||
4181 | } |
||
4182 | |||
4183 | При завершении процедуры прерывания будет автоматически сгенерирована |
||
4184 | инструкция выхода из обработчика прерывания - IRET. |
||
7544 | leency | 4185 | Return to contents. |
7496 | leency | 4186 | |
4187 | |||
7544 | leency | 4188 | |
7496 | leency | 4189 | 11.7 Замена return на goto. |
7544 | leency | 4190 | |
7496 | leency | 4191 | |
4192 | В некоторых ситуациях, при компиляции программы, оператор return |
||
4193 | будет заменяться на goto. Это происходит при разрешенной оптимизации по |
||
4194 | размеру кода для операторов return, которые расположены внутри процедуры |
||
4195 | и, естественно, если размер кода для выполнения return больше, чем размер |
||
4196 | кода для реализации goto. Для динамических процедур, которые используются |
||
4197 | как макросы, такая замена будет производится всегда. Оператор goto будет |
||
4198 | выполнен на конец процедуры, там, где будет располагаться единственный |
||
4199 | выход из процедуры. В динамических процедурах, используемых в качестве |
||
4200 | макросов, return в конце процедуры будет пропущен компилятором. |
||
4201 | |||
4202 | Таким образом, снято последнее ограничение на использование |
||
4203 | динамических процедур в качестве макросов. Любая динамическая процедура |
||
4204 | может быть использована как макрос. |
||
4205 | |||
4206 | Для оператора goto существует его более короткий аналог - GOTO. |
||
4207 | Для получения более компактного кода для оператора return введен также |
||
4208 | более короткий оператор RETURN. Его можно использовать, если от места |
||
4209 | его применения до конца процедуры находится не более 128 байт. Если Вы |
||
4210 | будете использовать RETURN на большем расстоянии до конца процедуры, то |
||
4211 | компилятор выдаст сообщение об ошибке. При использовании return на |
||
4212 | расстоянии меньше 128 байт до конца кода, компилятор выдаст вам |
||
4213 | предупреждение о возможном использовании RETURN. |
||
7544 | leency | 4214 | Return to contents. |
7496 | leency | 4215 | |
4216 | |||
7544 | leency | 4217 | |
7496 | leency | 4218 | 11.8 Возвращаемые значения. |
7544 | leency | 4219 | |
7496 | leency | 4220 | |
4221 | Возвращаемые из функций значения располагаются в регистрах. В таблице |
||
4222 | показано, какой регистр используется для каждого из возвращаемых типов: |
||
4223 | |||
4224 | -------------------------------------------- |
||
4225 | | возвращаемый тип | используемый регистр | |
||
4226 | -------------------------------------------- |
||
4227 | | byte | AL | |
||
4228 | | word | AX | |
||
4229 | | dword | EAX | |
||
4230 | | char | AL | |
||
4231 | | int | AX | |
||
4232 | | long | EAX | |
||
4233 | | float | EAX | |
||
4234 | -------------------------------------------- |
||
4235 | |||
4236 | Самый простой способ вернуть значение из функции состоит в том, чтобы |
||
4237 | использовать команду return(), но вместо этого можно напрямую загрузить |
||
4238 | возвращаемое значение в соответствующий регистр. Например, следующие две |
||
4239 | функции возвращают одно и то же значение: |
||
4240 | |||
4241 | byte proc_one () |
||
4242 | { |
||
4243 | return (42); |
||
4244 | } |
||
4245 | |||
4246 | byte proc_two () |
||
4247 | { |
||
4248 | AL = 42; |
||
4249 | } |
||
4250 | |||
4251 | Многие DOS функции 0x21 прерывания в качестве индикатора успешного |
||
4252 | выполнения используют установку или сброс carry флага. Использовать флаги |
||
4253 | процессора при возврате из процедур можно и в других случаях, когда надо |
||
4254 | иметь статус успешного или не успешного выполнения процедуры. Это позволит |
||
4255 | более полно использовать возможности процессора и соответственно уменьшит |
||
4256 | размер кода и повысит быстродействие программы. |
||
4257 | |||
4258 | Наряду с флагами, при возврате из процедур, по прежнему остается |
||
4259 | возврат различных типов и через регистр AL/AX/EAX. Если для процедуры |
||
4260 | объявлено, что она имеет тип возврата int и CARRYFLAG, то при использовании |
||
4261 | такой процедуры в операциях сравнения IF, WHILE... будет делаться проверка |
||
4262 | carry флага, а не сравнение регистра AX. Пример использования возврата |
||
4263 | флагов из процедур: |
||
4264 | |||
7544 | leency | 4265 | int CARRYFLAG FOPEN(); // объявление процедуры |
7496 | leency | 4266 | |
4267 | void proc() |
||
4268 | { |
||
4269 | IF ( FOPEN(name,0) ) Error ( "Not open file" ); |
||
4270 | } |
||
4271 | |||
4272 | Варианты допустимого синтаксиса для использования возврата флага: |
||
4273 | |||
4274 | IF ( ! FOPEN() )... |
||
4275 | IF ( @ FOPEN() )... |
||
4276 | IF ( ! @ FOPEN() )... |
||
4277 | IF ( handl = FOPEN() )... |
||
4278 | IF ( handl = @ FOPEN() )... |
||
4279 | IF ( ! handl = FOPEN() )... |
||
4280 | IF ( ! handl = @ FOPEN() )... |
||
4281 | |||
4282 | А вот варианты, в которых, несмотря на то, что для процедуры объявлен |
||
4283 | возврат флага, будет производиться сравнение регистра AX: |
||
4284 | |||
7544 | leency | 4285 | IF ( FOPEN() == 5 )... // производится сравнение |
7496 | leency | 4286 | IF ( FOPEN() + 2 )... // результат процедуры подвергается дальнейшему |
4287 | // вычислению, в результате которого флаги будут |
||
7544 | leency | 4288 | // изменены. |
4289 | Return to contents. |
||
7496 | leency | 4290 | |
4291 | |||
7544 | leency | 4292 | |
7496 | leency | 4293 | 11.9 Объявление параметров в регистровых процедурах. |
7544 | leency | 4294 | |
7496 | leency | 4295 | |
4296 | Ранее каждому параметру регистровой процедуры соответствовал строго |
||
4297 | определенный регистр. Например, для переменных типа int или word первый |
||
4298 | параметр передавался через регистр AX, 2-й - BX, 3-й - CX, 4-й - DX, 5-й - |
||
4299 | DI, 6-й - SI. Поэтому, если Вам было необходимо передать только один |
||
4300 | параметр через регистр SI, то приходилось перед ним писать пять запятых. |
||
4301 | Вот как, например, выглядит вызов процедуры STRCPY: |
||
4302 | |||
4303 | void main () |
||
4304 | { |
||
4305 | STRCPY ( , , , , #dest, #sourc ) ; |
||
4306 | } |
||
4307 | |||
4308 | Теперь регистры могут располагаться при передаче параметров |
||
4309 | произвольным образом. Надо только объявить компилятору о том, какой регистр |
||
4310 | закреплен за каким параметром данной процедуры. После такого объявления |
||
4311 | компилятор будет сам следить за тем, через какой регистр передавать |
||
4312 | параметр процедуре, его размерностью и числом передаваемых параметров. Вот |
||
4313 | как будет выглядеть объявление и использование процедуры STRCPY: |
||
4314 | |||
7544 | leency | 4315 | void STRCPY ( DI, SI ) ; //это объявление процедуры |
7496 | leency | 4316 | |
4317 | void main () |
||
4318 | { |
||
7544 | leency | 4319 | STRCPY ( #dest, #sourc ) ; //а это вызов процедуры |
7496 | leency | 4320 | } |
4321 | |||
4322 | Можно не делать объявления процедуры, а указать расположение регистров |
||
4323 | в заголовке процедуры. Но тогда такая процедура должна вызываться только |
||
4324 | после ее определения. Вот пример процедуры выводящей на экран несколько |
||
4325 | одинаковых символов: |
||
4326 | |||
4327 | void PUTNCHAR(AL,CX,BL,BH) |
||
4328 | /* 1 параметр в AL - код символа, который будет выведен |
||
4329 | 2 параметр в CX - число выводимых символов |
||
4330 | 3 параметр в BL - цветовой атрибут |
||
4331 | 4 параметр в BH - номер видеостраницы |
||
4332 | */ |
||
4333 | { |
||
4334 | AH=9; |
||
4335 | $INT 0x10 |
||
4336 | } |
||
4337 | |||
4338 | При объявлении регистровой процедуры можно также указывать какой тип |
||
4339 | переменной ожидает процедура (знаковый/без знаковый или вещественный). По |
||
4340 | умолчанию считается без знаковый тип. Однако знаковый тип указывать есть |
||
4341 | смысл только если параметр передается через регистр AL/AX/EAX. Через другие |
||
4342 | регистры переменная всегда передается как без знаковая. Пример объявления |
||
4343 | регистровой процедуры с указанием типов: |
||
4344 | |||
4345 | int fastcall Exampl( word CX, int AX, DX, float ESI ) ; |
||
4346 | | | | | | | | |
||
4347 | | | | | | | |---- 4-й парам. имеет тип float и |
||
4348 | | | | | | | перед. через регистр ESI. |
||
4349 | | | | | | |-------- 3-й парам. имеет по умолч. |
||
4350 | | | | | | тип word и перед. через DX. |
||
4351 | | | | | |------------ 2-й парам. имеет тип int и |
||
4352 | | | | | передается через регистр AX. |
||
4353 | | | | |---------------------- 1-й парам. имеет тип word и |
||
4354 | | | | передается через регистр CX. |
||
4355 | | | |------------------------------- Имя объявляемой процедуры. |
||
4356 | | |---------------------------------------- Модификатор, указывающий, что |
||
4357 | | эта проц. явл. регистровой. |
||
4358 | |--------------------------------------------- Процедура возвращает перемен. |
||
7544 | leency | 4359 | типа int. |
7496 | leency | 4360 | |
4361 | Если Вы сделали объявление регистров процедуры, то компилятор будет |
||
4362 | строго следить за количеством указанных параметров при вызове этой |
||
4363 | процедуры и выдавать сообщения об ошибке, если их будет меньше или больше. |
||
4364 | С одной стороны это хорошо - есть контроль за тем, что Вы ничего не забыли |
||
4365 | или не добавили лишнего при вызове процедуры. С другой стороны иногда |
||
4366 | бывают необязательные параметры, а их теперь придется прописывать. Но если |
||
4367 | Вы при вызове процедуры не укажете ни одного параметра, то компилятор не |
||
4368 | будет Вам выдавать сообщение об ошибке. Это дает Вам возможность |
||
4369 | проинициализировать регистры, через которые Вы передаете параметры, вне |
||
4370 | вызова процедуры. Но если Вы укажете, хоть один параметр, то Вам придется |
||
4371 | указывать и остальные, иначе компилятор будет считать, что Вы их случайно |
||
4372 | пропустили и выдаст сообщение об ошибке. |
||
4373 | |||
4374 | Если Вы не объявили регистры ни при объявлении регистровой процедуры, |
||
4375 | ни в заголовке самой процедуры, то компилятор будет считать, что параметры |
||
4376 | в эту процедуру передаются старым способом. Таким образом, достигается |
||
4377 | полная совместимость с предыдущими версиями компилятора. |
||
7544 | leency | 4378 | Return to contents. |
7496 | leency | 4379 | |
4380 | |||
7544 | leency | 4381 | |
7496 | leency | 4382 | 11.10 Объявление параметров в стековых процедурах. |
7544 | leency | 4383 | |
7496 | leency | 4384 | |
4385 | Как известно, ранее в C-- контроль за числом и типом передаваемых |
||
4386 | процедуре параметров возлагался на программиста. Поэтому возникла непростая |
||
4387 | задача, совместить одновременно отсутствие контроля за параметрами (для |
||
4388 | совместимости с предыдущими версиями) и ее наличие. В результате |
||
4389 | компромиссов появился вариант немного отличающийся от традиционно принятого |
||
4390 | в языках C. |
||
4391 | |||
4392 | Главное отличие - это то, что параметры, определяемые при определении |
||
4393 | процедуры, не будут восприниматься компилятором для контроля за ними. Во |
||
4394 | всех языках C допускается совмещение прототипа процедуры и ее объявления. |
||
4395 | В C-- для того, чтобы включился контроль за параметрами стековой процедуры, |
||
4396 | надо эту процедуру обязательно объявить. Но не всякое объявление процедуры |
||
4397 | будет сигналом компилятору о включении контроля за параметрами этой |
||
4398 | процедуры. Если при объявлении в круглых скобках ничего не будет, то |
||
4399 | компилятор не будет отслеживать параметры, передаваемые этой процедуре. В |
||
4400 | C++ такое объявление означает, что процедуре не передаются никакие |
||
4401 | параметры. В C-- для этого надо при объявлении процедуры в круглых скобках |
||
4402 | обязательно написать void. Например: |
||
4403 | |||
4404 | int proc ( void ) ; |
||
4405 | |||
4406 | Встретив такое объявление процедуры, компилятор будет следить за тем, |
||
4407 | чтобы этой процедуре не были переданы параметры. |
||
4408 | |||
4409 | При объявлении процедуры имена параметров можно опускать. Как известно, |
||
4410 | в C-- параметры процедуры одного типа записываются через запятую. Для смены |
||
4411 | типа используют точку с запятой. При объявлении смену типа можно |
||
4412 | производить и после запятой: |
||
4413 | |||
4414 | void ptoc ( int a, b, c; word d ); |
||
4415 | void proc ( int, int, int, word ); |
||
4416 | void proc ( int, int, int; word ); |
||
4417 | |||
4418 | Все эти примеры объявлений являются идентичными и допустимыми. |
||
4419 | |||
4420 | Для контроля за процедурами с переменным числом параметров был введен |
||
4421 | новый для C-- элемент синтаксиса - многоточие или его еще называют эллипс. |
||
4422 | Вот как будет выглядеть объявление процедуры printf: |
||
4423 | |||
4424 | void cdecl printf ( word, ... ); |
||
7544 | leency | 4425 | Return to contents. |
7496 | leency | 4426 | |
4427 | |||
7544 | leency | 4428 | |
7496 | leency | 4429 | 11.11 Использование макрокоманд. |
7544 | leency | 4430 | |
7496 | leency | 4431 | |
4432 | Теперь любая динамическая процедура может быть использована как макрос. |
||
4433 | Если перед вызовом динамической процедуры поставить символ @, то код этой |
||
4434 | процедуры будет вставлен, а не вызван инструкцией CALL. |
||
4435 | |||
4436 | При использовании стековых динамических процедур в качестве макросов |
||
4437 | очистка стека от переданных параметров производится ассемблерной |
||
4438 | инструкцией ADD SP,SIZE_PARAMETRS сразу после окончания кода вставленного |
||
4439 | макроса. Поэтому, если эта процедура использовала флаги в качестве |
||
4440 | возврата, то они будут разрушены. |
||
7544 | leency | 4441 | Return to contents. |
7496 | leency | 4442 | |
4443 | |||
7544 | leency | 4444 | |
7496 | leency | 4445 | 11.12 Передача параметров в стековые процедуры через регистры. |
7544 | leency | 4446 | |
7496 | leency | 4447 | |
4448 | При передаче параметров через регистры, чаще всего получается более |
||
4449 | компактный и быстрый код. Но содержимое регистров может быть легко |
||
4450 | разрушено. Если в Вашей процедуре, какой-то из параметров используется |
||
4451 | однократно для того, чтобы в начале процедуры инициализировать какой-то |
||
4452 | регистр, то Вы можете передать это значение в процедуру сразу через |
||
4453 | регистр, минуя стадию засовывания и извлечения содержимого в стек. Пример: |
||
4454 | |||
4455 | int proc (int param1, param2, param3) |
||
4456 | { |
||
4457 | (E)BX = param3; |
||
4458 | (E)BX.TEG_STRUCT.var = proc2 (param1,papra2); |
||
4459 | proc3 (param1,param2); |
||
4460 | } |
||
4461 | |||
4462 | В этом примере параметр param3 используется лишь для того, чтобы |
||
4463 | инициализировать регистр (E)BX, поэтому его можно сразу передать через |
||
4464 | регистр: |
||
4465 | |||
4466 | int proc (int param1, param2, (E)BX) |
||
4467 | { |
||
4468 | (E)BX.TEG_STRUCT.var = proc2 (param1,papra2); |
||
4469 | proc3 (param1,param2); |
||
4470 | } |
||
4471 | |||
4472 | Как Вы видите, процедура немного упростилась. |
||
4473 | |||
4474 | В принципе, порядок расположения стековых и регистровых параметров не |
||
4475 | принципиален. Но надо помнить, что содержимое регистров может быть легко |
||
4476 | разрушено, и поэтому лучше всего регистровые параметры инициализировать |
||
4477 | лишь после того, как были засунуты в стек все стековые параметры. Для |
||
4478 | процедур типа pascal регистровые параметры лучше располагать после |
||
4479 | стековых параметров. Для процедур типа cdecl и stdcall сначала лучше |
||
4480 | располагать регистровые параметры. |
||
7544 | leency | 4481 | Return to contents. |
7496 | leency | 4482 | |
4483 | |||
7544 | leency | 4484 | |
7496 | leency | 4485 | 11.13 Вызов процедур с адресом в регистре. |
7544 | leency | 4486 | |
7496 | leency | 4487 | |
4488 | В C-- допустимо делать вызов процедуры, адрес которой находится в |
||
4489 | регистре. Параметры для такого вызова передаются только через стек. Тип |
||
4490 | вызова процедуры для программ под Windows stdcall, для остальных pascal. |
||
4491 | Адрес процедуры для 32-битных программ должен находится в 32-битном |
||
4492 | регистре, а для 16-битных программ в 16-битном регистре. Считается, что |
||
4493 | такой вызов имеет возврат типа unsigned int. Пример: |
||
4494 | |||
4495 | BX = # proc; |
||
4496 | BX (a); |
||
4497 | IF ( BX(b) == 0 ) AX=2; |
||
4498 | |||
4499 | Вы получите следующий код: |
||
4500 | |||
4501 | test.c-- 8: BX=#proc; |
||
4502 | 0104 BB1A01 mov bx,11Ah |
||
4503 | |||
4504 | test.c-- 9: BX(a); |
||
4505 | 0107 FF76FC push word ptr [bp-4] |
||
4506 | 010A FFD3 call near bx |
||
4507 | |||
4508 | test.c-- 10: IF (BX(b) == 0)AX=2; |
||
4509 | 010C FF76FE push word ptr [bp-2] |
||
4510 | 010F FFD3 call near bx |
||
4511 | 0111 85C0 test ax,ax |
||
4512 | 0113 7503 jne 118h |
||
4513 | 0115 B80200 mov ax,2 |
||
7544 | leency | 4514 | Return to contents. |
7496 | leency | 4515 | |
4516 | |||
7544 | leency | 4517 | |
7496 | leency | 4518 | 11.14 Встроенные в компилятор процедуры. |
7544 | leency | 4519 | |
7496 | leency | 4520 | |
4521 | Для некоторых процедур Вы не найдете их исходные тексты в библиотеках |
||
4522 | компилятора. Код этих процедур генерирует компилятор. Вот список этих |
||
4523 | процедур: |
||
4524 | |||
4525 | ABORT Прекращение выполнения программы |
||
4526 | atan Вычислить арктангенс числа |
||
4527 | atan2 Вычислить арктангенс числа |
||
4528 | ATEXIT Зарегистрировать функцию выполняющуюся при выходе. |
||
4529 | cos Возвращает косинус угла |
||
4530 | EXIT Закончить программу с кодом ошибки |
||
4531 | exp Возвращает экспоненту числа |
||
4532 | inp/inportb Считать один байт из порта |
||
4533 | inport Считать слово из порта |
||
4534 | inportd Считать двойное слово из порта |
||
4535 | fabs Возвращает абсолютное значение числа |
||
4536 | log Вычисляет натуральный логарифм числа |
||
4537 | log10 Вычисляет десятичный логарифм числа |
||
4538 | outp/outportb Записать один байт в порт |
||
4539 | outport Записать слово в порт |
||
4540 | outportd Записать двойное слово в порт |
||
4541 | sin Возвращает синус угла |
||
4542 | sqrt Извлечь квадратный корень через FPU. |
||
4543 | tan Возвращает тангенс угла |
||
4544 | |||
4545 | Размещение этих процедур непосредственно в компиляторе, связано с тем, |
||
4546 | что в настоящий момент компилятор может таким образом генерировать более |
||
4547 | эффективный код, чем если бы эти процедуры располагались в библиотеках. |
||
4548 | В будущем, по мере развития компилятора, эти процедуры постепенно будут |
||
4549 | выносится из компилятора в библиотеки. |
||
4550 | |||
4551 | Но ничто не мешает Вам уже сейчас написать свои одноименные |
||
4552 | библиотечные процедуры. Встретив определение такой процедуры, компилятор не |
||
4553 | будет выдавать никаких сообщение, он просто будет применять Ваш вариант |
||
4554 | процедуры. |
||
7544 | leency | 4555 | Return to contents. |
7496 | leency | 4556 | |
4557 | |||
7544 | leency | 4558 | |
7496 | leency | 4559 | 11.14.1 Процедуры ABORT, ATEXIT и EXIT. |
7544 | leency | 4560 | |
7496 | leency | 4561 | |
4562 | Процедуры ABORT и EXIT связаны с работой директивы #atexit и |
||
4563 | процедурой ATEXIT. Наиболее оптимальную их реализацию и взаимную |
||
4564 | интеграцию может сделать только компилятор. Именно поэтому эти процедуры |
||
4565 | поддерживаются компилятором. |
||
4566 | |||
4567 | Процедура ATEXIT - регистровая процедура, которая регистрирует |
||
4568 | функцию, адрес которой передается ей в качестве параметра, т.е. через |
||
4569 | регистр (E)AX, как функцию завершения программы. При успешной регистрации |
||
4570 | ATEXIT возвращает 0. Всего можно зарегистрировать до 16 функций. |
||
4571 | |||
4572 | Завершающие функции не должны иметь параметров и возврата. Эти |
||
4573 | функции будут выполняться в порядке обратном очередности регистрации в |
||
4574 | случае, если Вы будете завершать работу программы через вызовы процедур |
||
4575 | ABORT или EXIT или закончится работа процедуры main. Если Вы |
||
4576 | завершите работу программы вызовом процедуры ExitProcess под Windows или |
||
4577 | вызовом AH=0x4C; $int 0x21 под DOS, выход из программы произойдет без |
||
4578 | запуска зарегистрированных функций. |
||
4579 | |||
4580 | Процедура ABORT и EXIT, если не включена директива #atexit делают |
||
4581 | вызов процедуры ExitProcess под Windows и вызов AH=0x4C; $int 0x21 под |
||
4582 | DOS. Процедуре ABORT не передаются никакие параметры, и она завершает |
||
4583 | работу программы с кодом возврата 0. Процедуре EXIT передается в |
||
4584 | качестве параметра код возврата, с которым она и завершает работу |
||
4585 | программы. |
||
7544 | leency | 4586 | Return to contents. |
7496 | leency | 4587 | |
4588 | |||
7544 | leency | 4589 | |
7496 | leency | 4590 | 11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и |
4591 | outportd |
||
7544 | leency | 4592 | |
7496 | leency | 4593 | |
4594 | Эти процедуры всегда вставляются в код как макросы, т.е. для этих |
||
4595 | процедур никогда не генерируется вызов процедуры. В зависимости от |
||
4596 | значения порта, с которым работают эти процедуры, генерируется разный |
||
4597 | код. Все это позволяет получать более компактный код. |
||
4598 | |||
4599 | Процедуры чтения из порта имеют такой прототип: |
||
4600 | |||
4601 | byte inp ( word port ); |
||
4602 | word inport ( word port ); |
||
4603 | dword inportd ( word port ); |
||
4604 | |||
4605 | Процедуры записи в порт имеют такой прототип: |
||
4606 | |||
4607 | void outp ( byte val; word port ); |
||
4608 | void outport ( word val; word port ); |
||
4609 | void outportd ( dword val; word port ); |
||
4610 | |||
4611 | Имена процедур inp и inportb, также как и имена outp и outportb |
||
4612 | являются синонимами. |
||
7544 | leency | 4613 | Return to contents. |
7496 | leency | 4614 | |
4615 | |||
7544 | leency | 4616 | |
7496 | leency | 4617 | 11.14.3 Процедуры для работы с вещественными числами. |
7544 | leency | 4618 | |
7496 | leency | 4619 | |
4620 | Эти процедуры реализуются компилятором и всегда вставляются в код как |
||
4621 | макросы, т.е. для них никогда не генерируется вызов процедуры. Кроме |
||
4622 | этого, если параметром одной процедуры является вызов другой, то |
||
4623 | результат работы второй процедуры остается в стеке FPU, а первая |
||
4624 | процедура использует этот результат непосредственно из стека. Таким |
||
4625 | образом получаются более компактный код. Вот вымышленный пример: |
||
4626 | |||
4627 | test.c-- 7: f = sin( sqrt(1) ); |
||
4628 | 0100 D9061C01 fld [11Ch] |
||
4629 | 0104 D9FA fsqrt |
||
4630 | 0106 D9FE fsin |
||
4631 | 0108 D91E2001 fstp [120h] |
||
4632 | 010C 9B fwait |
||
4633 | |||
4634 | Эти процедуры имеют следующий прототип: |
||
4635 | |||
4636 | float atan ( float val ); |
||
4637 | float atan ( float val, val2 ); |
||
4638 | float cos ( float val ); |
||
4639 | float exp ( float val ); |
||
4640 | float fabs ( float val ); |
||
4641 | float log ( float val ); |
||
4642 | float log10 ( float val ); |
||
4643 | float sin ( float val ); |
||
4644 | float sqrt ( float val ); |
||
4645 | float tan ( float val ); |
||
7544 | leency | 4646 | Return to contents. |
7496 | leency | 4647 | |
4648 | |||
7544 | leency | 4649 | |
7496 | leency | 4650 | 11.15 Классы. |
4651 | |||
4652 | 11.15.1 Объявление процедур в структурах. |
||
7544 | leency | 4653 | |
7496 | leency | 4654 | |
4655 | С введение поддержки объявления процедур в структурах, структура |
||
4656 | становится подобной классу в C++. Т.е. такая процедура становится методом |
||
4657 | класса. Пример: |
||
4658 | |||
4659 | struct Point // объявление класса |
||
4660 | { |
||
7544 | leency | 4661 | int x; // элементы данных |
4662 | int y; // класса типа Point |
||
4663 | void SetX(int); // объявление методов |
||
4664 | void SetY(int); // класса Point |
||
7496 | leency | 4665 | }; |
4666 | |||
4667 | void Point::SetX(int _x) //определение процедуры класса Point |
||
4668 | { |
||
7544 | leency | 4669 | IF((_x>=0)&&(_x<=MAX_X)) x=_x; |
7496 | leency | 4670 | // переменные x, y являются членами этого класса и поэтому доступ к ним из |
4671 | // процедур этого же класса осуществляется напрямую. |
||
4672 | } |
||
4673 | |||
4674 | void main() |
||
4675 | Point p; //определяем структуру в стеке |
||
4676 | { |
||
4677 | p.y = p.x = 0; |
||
4678 | p.SetX(1); |
||
4679 | } |
||
4680 | |||
4681 | При вызове процедуры являющейся методом класса ей неявным образом |
||
4682 | передается адрес этого класса (структуры). В самой процедуре этот адрес |
||
4683 | доступен через имя параметрической переменной this. Эту переменную |
||
4684 | автоматически генерирует компилятор. Если в объявление процедуры в |
||
4685 | структуре указать ключевое слово static, то такой процедуре адрес |
||
4686 | класса не передается и переменная this не генерируется. |
||
4687 | |||
4688 | Процедура объявленная в структуре может быть динамической. Для этого, |
||
4689 | при ее определении, в самом ее начале, надо написать символ двоеточия : |
||
4690 | (также как и для обычных динамических процедур). Но такая динамическая |
||
4691 | процедура не может быть использована как макрос. |
||
7544 | leency | 4692 | Return to contents. |
7496 | leency | 4693 | |
4694 | |||
7544 | leency | 4695 | |
7496 | leency | 4696 | 11.15.2 Наследование. |
7544 | leency | 4697 | |
7496 | leency | 4698 | |
4699 | В C-- поддерживаются простые и множественные наследования. Объявление |
||
4700 | структуры с наследованием имеет следующий синтаксис: |
||
4701 | |||
4702 | struct Derived : Base1, Base2, ... Basen |
||
4703 | { |
||
4704 | int x0; |
||
4705 | }; |
||
4706 | |||
4707 | Число базовых структур в производном не ограничено. При множественном |
||
4708 | наследовании структура может наследовать два и более экземпляра базовой |
||
4709 | структуры. При этом возникает неоднозначность. Пример: |
||
4710 | |||
4711 | struct A |
||
4712 | { |
||
4713 | int x,y; |
||
4714 | . . . |
||
4715 | }; |
||
4716 | |||
4717 | struct B : A //структура B наследует A |
||
4718 | { |
||
4719 | . . . |
||
4720 | |||
4721 | }; |
||
4722 | |||
4723 | struct C : A //структура C наследует A |
||
4724 | { |
||
4725 | . . . |
||
4726 | }; |
||
4727 | |||
4728 | struct D : B, C //структура D наследует B и C |
||
4729 | { |
||
4730 | . . . |
||
4731 | }; |
||
4732 | |||
4733 | void main() |
||
4734 | D d; //выделяем для структуры D память в стеке и присваиваем ей имя d |
||
4735 | { |
||
4736 | d.x0=0; |
||
4737 | |||
4738 | В этом примере структура D наследует два экземпляра структуры A и |
||
4739 | в ней находятся два элемента с именем x0. Компиляторы C++ при записи |
||
4740 | типа d.x0=0 выдают сообщение об ошибке. C-- эту запись обрабатывает, |
||
4741 | присваивание производится по умолчанию в элемент из последней базовой |
||
4742 | структуры, имеющей элемент x0. Для того чтобы получить доступ ко |
||
4743 | второму элементу x0 (физически этот элемент находится в структуре |
||
4744 | первым), необходимо применить операцию разрешения видимости: |
||
4745 | |||
4746 | d.B::x0=0; |
||
4747 | |||
4748 | Из всего этого следует, что записи: |
||
4749 | |||
4750 | d.x0=0; |
||
4751 | и |
||
4752 | d.C::x0=0; |
||
4753 | |||
4754 | являются равнозначными. |
||
7544 | leency | 4755 | Return to contents. |
7496 | leency | 4756 | |
4757 | |||
7544 | leency | 4758 | |
7496 | leency | 4759 | 11.15.3 Наследование процедур. |
7544 | leency | 4760 | |
7496 | leency | 4761 | |
7544 | leency | 4762 | Если в базовом классе есть процедура, а в производном классе Вы эту |
7496 | leency | 4763 | процедуру переопределили, то эта процедура будет переопределена и в |
4764 | базовом классе. Таким образом процедура определенная в базовом классе |
||
4765 | будет потеряна. Пример: |
||
4766 | |||
4767 | struct Point // базовый класс |
||
4768 | { |
||
7544 | leency | 4769 | int x; // элементы данных |
4770 | int y; // класса типа Point |
||
4771 | void SetX(int); // объявление методов |
||
4772 | void SetY(int); // класса Point |
||
7496 | leency | 4773 | }; |
4774 | |||
4775 | void Point::SetX(int _x) // определение процедуры класса Point |
||
4776 | { |
||
7544 | leency | 4777 | IF((_x>=0)&&(_x<=MAX_X)) x=_x; |
7496 | leency | 4778 | } |
4779 | |||
4780 | struct Point2 : Point // производный класс |
||
4781 | { |
||
4782 | int x2; |
||
4783 | } |
||
4784 | |||
4785 | struct Point3 : Point // еще один производный класс |
||
4786 | { |
||
4787 | int z; |
||
4788 | } |
||
4789 | |||
4790 | void Point3::SetX(int _x) // в этом производном классе переопределяем |
||
4791 | { // процедуру SetX |
||
7544 | leency | 4792 | IF((_x>=80)&&(_x<=MAX_X)) x=_x; |
7496 | leency | 4793 | } |
4794 | |||
7544 | leency | 4795 | Процедура SetX, определенная в базовом классе Point, теперь будет |
7496 | leency | 4796 | недоступна. Вместо кода определенного в этом классе, будет вызываться код |
4797 | процедуры, определенный в наследуемом классе Point3. При вызове процедуры |
||
4798 | SetX из другого производного класса Point2 будет также вызываться код |
||
4799 | процедуры, определенный в производном классе Point3. Переопределяя |
||
4800 | процедуру таким образом, Вы замените код этой процедуры в базовом классе и |
||
4801 | во всех его наследуемых классах. |
||
4802 | |||
7544 | leency | 4803 | Если Вам необходимо, чтобы код новой процедуры был доступен |
7496 | leency | 4804 | одновременно с кодом старой процедуры, то в производном классе Вам |
4805 | необходимо сделать еще одно объявление этой процедуры. Пример: |
||
4806 | |||
4807 | struct Point // базовый класс |
||
4808 | { |
||
7544 | leency | 4809 | int x; // элементы данных |
4810 | int y; // класса типа Point |
||
4811 | void SetX(int); // объявление методов |
||
4812 | void SetY(int); // класса Point |
||
7496 | leency | 4813 | }; |
4814 | |||
4815 | void Point::SetX(int _x) // определение процедуры класса Point |
||
4816 | { |
||
7544 | leency | 4817 | IF((_x>=0)&&(_x<=MAX_X)) x=_x; |
7496 | leency | 4818 | } |
4819 | |||
4820 | struct Point2 : Point // производный класс |
||
4821 | { |
||
4822 | int x2; |
||
4823 | } |
||
4824 | |||
4825 | struct Point3 : Point // еще один производный класс |
||
4826 | { |
||
4827 | int z; |
||
4828 | void SetX(int); // в наследуемом классе делаем еще одно объявление |
||
4829 | // процедуры SetX |
||
4830 | } |
||
4831 | |||
4832 | void Point3::SetX(int _x) // в этом производном классе переопределяем |
||
4833 | { // процедуру SetX |
||
7544 | leency | 4834 | IF((_x>=80)&&(_x<=MAX_X)) x=_x; |
4835 | EDI=this; |
||
4836 | EDI.Point.SetX(_x); // делаем вызов одноименной процедуры из |
||
4837 | // базового класса |
||
7496 | leency | 4838 | } |
4839 | |||
7544 | leency | 4840 | Теперь из производного класса Point3 Вам доступны две различные |
7496 | leency | 4841 | процедуры с одним именем SetX. А из базового класса Point и из другого |
4842 | производного класса Point2 будет по прежнему доступен только базовый |
||
4843 | вариант процедуры SetX. |
||
7544 | leency | 4844 | Return to contents. |
7496 | leency | 4845 | |
4846 | |||
7544 | leency | 4847 | |
7496 | leency | 4848 | 12. Типы выходных файлов. |
4849 | |||
4850 | 12.1 Выходные файлы типа COM. |
||
7544 | leency | 4851 | |
7496 | leency | 4852 | |
4853 | Этот тип выходного файла получается автоматически по умолчанию. |
||
4854 | |||
4855 | Изначально C-- мог делать только файлы формата типа COM. В настоящее |
||
4856 | время появилась возможность получать файла типа EXE с моделями памяти tiny |
||
4857 | и small для 16-битного кода, а также 32-битные для DOS и Windows. Также |
||
4858 | есть возможность получения выходного файла в формате OBJ, что позволяет |
||
4859 | связывать программы на C-- с программами на других языках. |
||
7544 | leency | 4860 | Return to contents. |
7496 | leency | 4861 | |
4862 | |||
7544 | leency | 4863 | |
7496 | leency | 4864 | 12.2 Выходные файлы типа EXE. |
7544 | leency | 4865 | |
7496 | leency | 4866 | |
4867 | Этот формат файла можно получить, если компилировать с ключом командной |
||
4868 | строки /exe или /e. |
||
4869 | |||
4870 | Возможно также поддержка EXE-формата через выходной файл формата OBJ, |
||
4871 | который можно затем обработать линковщиком, не входящим в пакет C--. |
||
7544 | leency | 4872 | Return to contents. |
7496 | leency | 4873 | |
4874 | |||
7544 | leency | 4875 | |
7496 | leency | 4876 | 12.3 Выходной файл *.EXE с моделью памяти tiny. |
7544 | leency | 4877 | |
7496 | leency | 4878 | |
4879 | Фактически код файла *.exe модели tiny ничем не отличается от кода |
||
4880 | *.com. В сущности, это тот же com-файл, к которому добавлен 32-байтный |
||
4881 | заголовок exe-файла. Единственное отличие возникает, когда Вы компилируете |
||
4882 | файл с директивой ?resize TRUE. В com-файле, по этой директиве, в код |
||
4883 | программы добавляется соответствующий код, изменяющий размер доступной |
||
4884 | памяти. В exe-файле для этих целей будет скорректирован заголовок |
||
4885 | exe-файла. |
||
4886 | |||
4887 | Чтобы получить exe-файл с моделью памяти tiny, надо запустить |
||
4888 | компилятор с ключом в командной строке /TEXE. |
||
7544 | leency | 4889 | Return to contents. |
7496 | leency | 4890 | |
4891 | |||
7544 | leency | 4892 | |
7496 | leency | 4893 | 12.4 Объектный выходной файл OBJ. |
7544 | leency | 4894 | |
7496 | leency | 4895 | |
4896 | В настоящее время C-- может только создавать OBJ-файлы, но не может их |
||
4897 | компоновать. |
||
4898 | |||
4899 | Ранее C-- создавал obj-файлы, которые могли быть подключены к проектам |
||
4900 | созданным на других языках, т.е. ведомые (slave) модули. Причем из C-- |
||
4901 | модулей для основного проекта были доступны только процедуры и эти |
||
4902 | процедуры не должны были использовать глобальные переменные. |
||
4903 | |||
4904 | Теперь же C-- может создавать основной модуль (master), который может |
||
4905 | быть слинкован в самостоятельный файл. |
||
4906 | |||
4907 | Для obj-файлов появилась возможность использовать внешние (extern) |
||
4908 | процедуры, переменные или структуры. Для этого достаточно их объявить как |
||
4909 | extern. Причем ключевое слово extern должно быть всегда первым. Пример |
||
4910 | объявления внешних объектов: |
||
4911 | |||
4912 | extern void cdecl _printf(); // объявление внешней процедуры _printf имеющей |
||
4913 | // тип cdecl и тип возврата void |
||
4914 | extern int buts,cubs; // объявление двух внешних переменных типа int |
||
4915 | extern struct IPXL ipxl; // объявление внешней структуры ipxl имеющей тег |
||
7544 | leency | 4916 | // IPXL, причем тег этой структуры должен быть |
4917 | // описан ранее. |
||
7496 | leency | 4918 | |
4919 | Появление возможности объявлять внешние объекты позволяет подключать к |
||
4920 | obj-модулю на C-- модули написанные на других языках или подключать к |
||
4921 | программе на C-- процедуры из библиотек на других языках. При объявлении |
||
4922 | внешних объектов очень важно правильно указать тип процедуры и ее имя. Если |
||
4923 | Вы будете использовать внешние процедуры, написанные на C то чаще всего, |
||
4924 | Вам нужно будет указывать модификатор cdecl, а к имени процедуры или |
||
4925 | переменной добавлять префикс _. |
||
4926 | |||
4927 | Из основного (master) obj-файла написанного на C-- для других |
||
4928 | obj-модулей доступны все процедуры, глобальные переменные и глобальные |
||
4929 | структуры. |
||
4930 | |||
4931 | Чтобы получить ведомый obj-модуль при компиляции надо использовать ключ |
||
4932 | /sobj. |
||
4933 | |||
4934 | C-- может создавать obj-файлы с моделью памяти tiny и small. По |
||
4935 | умолчанию создаются модули с моделью tiny. Чтобы получить obj-файл с |
||
4936 | моделью памяти small надо запустить компилятор с ключами /obj и /exe. |
||
4937 | |||
4938 | Для создания obj-файлов для 32-битного DOS в командной строке Вам |
||
4939 | необходимо указать ключи /d32 и /obj. Использовать полученный obj-файл мне |
||
4940 | удалось лишь с помощью wlink и расширителя zrdx.exe. |
||
4941 | |||
4942 | Создание obj-файлов под windows не предусмотрено. |
||
7544 | leency | 4943 | Return to contents. |
7496 | leency | 4944 | |
4945 | |||
7544 | leency | 4946 | |
7496 | leency | 4947 | 12.5 COM файл symbiosis. |
4948 | |||
4949 | 12.5.1 СИМБИОЗ - что это такое? |
||
7544 | leency | 4950 | |
7496 | leency | 4951 | |
4952 | Транслятор C-- имеет ключ, позволяющий добавлять компилируемую |
||
4953 | программу к концу уже имеющегося COM файла. Это называют COM-файл |
||
4954 | Symbiosis. Когда такая программа запускается, управление сначала получает |
||
4955 | добавленный код C--, и только после выполнения его процедуры main() |
||
4956 | управление получит первоначальный код COM-файла. |
||
4957 | |||
4958 | Если добавленный вами код завершается EXIT() или ABORT(), программа |
||
4959 | прекратится, и первоначальный код COM-файла не будет выполнен. Это |
||
4960 | позволяет программе, добавленной к COM файлу, определять, будет ли |
||
4961 | управление передано на первоначальный код. |
||
7544 | leency | 4962 | Return to contents. |
7496 | leency | 4963 | |
4964 | |||
7544 | leency | 4965 | |
7496 | leency | 4966 | 12.5.2 Как это делать. |
7544 | leency | 4967 | |
7496 | leency | 4968 | |
4969 | Чтобы сделать это, Вы должны использовать ключ /SYM в командной |
||
4970 | строке компилятора, в которой указывается полное имя COM-файла, к |
||
4971 | которому что-то добавляется. При этом оригинал COM-файла не меняется, а |
||
4972 | новый файл содержит его в себе. Например, чтобы откомпилировать программу |
||
4973 | HELLO.C-- к концу копии C:\command.сом используют следующую команду: |
||
4974 | |||
4975 | C-- /SYM C:\COMMAND.COM HELLO.C-- |
||
4976 | |||
4977 | Будет создан выходной файл HELLO.COM . |
||
7544 | leency | 4978 | Return to contents. |
7496 | leency | 4979 | |
4980 | |||
7544 | leency | 4981 | |
7496 | leency | 4982 | 12.5.3 Использование. |
7544 | leency | 4983 | |
7496 | leency | 4984 | |
4985 | Вы можете, вероятно, придумать большое количество путей использования |
||
4986 | этой функции, типа: |
||
4987 | |||
4988 | - Добавление защиты с использованием пароля к некоторым |
||
7544 | leency | 4989 | специальным COM файлам. |
7496 | leency | 4990 | - Уменьшение памяти, доступной COM файлу при запуске. |
4991 | - Инициализация режима видео для COM файла. |
||
7544 | leency | 4992 | Return to contents. |
7496 | leency | 4993 | |
4994 | |||
7544 | leency | 4995 | |
7496 | leency | 4996 | 12.5.4 Злоупотребления. |
7544 | leency | 4997 | |
7496 | leency | 4998 | |
4999 | Любой злоумышленник может придумать и вредные применения для этой |
||
5000 | функции. Наиболее очевидное из них - создание троянских коней. Я хотел бы |
||
5001 | указать, что это неконструктивное использование C--, и любое |
||
5002 | разрушительное использование симбиозов COM-файлов запрещено. |
||
7544 | leency | 5003 | Return to contents. |
7496 | leency | 5004 | |
5005 | |||
7544 | leency | 5006 | |
7496 | leency | 5007 | 12.6 SYS - драйверы устройств. |
7544 | leency | 5008 | |
7496 | leency | 5009 | |
5010 | Компилятор значительно облегчит Ваш труд при написании драйверов. |
||
5011 | Компилятор сам создаст заголовок драйвера и процедуры СТРАТЕГИЯ и |
||
5012 | ПРЕРЫВАНИЕ. Вам остается лишь написать код обработки команд. |
||
5013 | |||
5014 | Что бы откомпилировать файл драйвера устройства, надо добавить в |
||
5015 | командную строку ключ /SYS. Кроме того, появились новые директивы |
||
5016 | компилятору, которые действуют только с этим ключом. Вот они: |
||
5017 | |||
5018 | ?sysattribute значение - эта директива передает компилятору |
||
5019 | атрибут создаваемого драйвера. По умолчанию устанавливается значение |
||
5020 | 0x2000. |
||
5021 | |||
5022 | ?sysname <текстовая строка> - эта директива передает компилятору |
||
5023 | имя будущего драйвера. По умолчанию присваивается имя "NO_NAME". Длина |
||
5024 | имени не более 8 символов. |
||
5025 | |||
5026 | ?syscommand command_0,command_1, ... command_n; - эта директива |
||
5027 | является обязательной. По этой директиве компилятору передается список имен |
||
5028 | процедур обработки команд драйвера. Имена разделены запятыми. Список должен |
||
5029 | заканчиваться символом точка-с-запятой. Можно передать не более 25 команд. |
||
5030 | Если какая-то команда не имеет кода поддержки, то в список надо записать |
||
5031 | слово NONE. |
||
5032 | |||
5033 | По умолчанию компилятор для драйвера не создает стек. Драйвер может |
||
5034 | пользоваться системным стеком. Но, говорят, что он имеет маленькую глубину. |
||
5035 | Если Ваши процедуры активно используют стек, и Вы не надеетесь на системный, |
||
5036 | то директивой ?stack <величина> можно заставить драйвер пользоваться своим |
||
5037 | стеком. |
||
5038 | |||
5039 | Вашим процедурам обработки команд при передаче управления в регистрах |
||
5040 | ES:BX будет передан адрес заголовка запроса. Регистр DS равен CS. При |
||
5041 | возврате управления ваши процедуры должны сохранить регистр DS. В регистре |
||
5042 | AX должен находиться код возврата. Остальные регистры могут быть |
||
5043 | использованы произвольным образом. |
||
5044 | |||
5045 | Процедуру обработки команды инициализации желательно располагать |
||
5046 | последней (чтобы иметь возможность отдать адресное пространство занимаемое |
||
5047 | этой процедурой операционной системе). Перед этой процедурой, если Вы в |
||
5048 | других процедурах обработки команд используете динамические процедуры, |
||
5049 | обязательно должна быть директива ?setdinproc. Глобальные переменные должны |
||
5050 | быть обязательно проинициализированы. |
||
7544 | leency | 5051 | Return to contents. |
7496 | leency | 5052 | |
5053 | |||
7544 | leency | 5054 | |
7496 | leency | 5055 | 12.7 Компиляция кода расширителей ROM-BIOS. |
7544 | leency | 5056 | |
7496 | leency | 5057 | |
5058 | Расширители ROM-BIOS (BIOS видеоконтроллеров, сетевых карт...) имеют |
||
5059 | определенную структуру и требования. C-- теперь может облегчить Вам процесс |
||
5060 | создания кода ROM-BIOS. Если запустить компилятор на компиляцию с ключом |
||
5061 | командной строки /ROM, то компилятор создаст сигнатуру (заголовок) |
||
5062 | ROM-BIOS, заполнит оставшееся свободное место до указанного размера ПЗУ |
||
5063 | кодом заполнения, подсчитает и скорректирует контрольную сумму ПЗУ. |
||
5064 | |||
5065 | Для этого режима компиляции есть несколько специфических директив: |
||
5066 | |||
5067 | 1. ?sizerom value - эта директива сообщает компилятору размер ПЗУ в |
||
5068 | байтах. Если эта директива не указана, то компилятор сам выберет |
||
5069 | минимальный подходящий размер ПЗУ из ряда: 1024, 2048, 4096, 8192, 16384, |
||
5070 | 32762 или 65536. Свободное от кода и данных место будут заполнены до конца |
||
5071 | размера ПЗУ байтом заполнения определяемого директивой ?aligner. По |
||
5072 | умолчанию он равен нулю, для РПЗУ типа 27ххх этот байт имеет смысл сделать |
||
5073 | равным 0xFF. Последний байт ПЗУ будет скорректирован компилятором таким |
||
5074 | образом, чтобы контрольная сумма равнялась нулю. |
||
5075 | |||
5076 | 2. ?movedatarom TRUE/FALSE - эта директива сообщает компилятору есть ли |
||
5077 | необходимость копировать данные из ПЗУ в ОЗУ. По умолчанию она установлена |
||
5078 | в FALSE. Если эту директиву определить TRUE, то компилятор вставит в |
||
5079 | область инициализации код перемещающий данные из ПЗУ в ОЗУ. При этом |
||
5080 | регистр DS будет установлен на сегмент ОЗУ. Стек также будет переустановлен |
||
5081 | на этот сегмент. Таким образом, процедура main получит управление с |
||
5082 | регистрами AX = ES = DS = SS = сегменту ОЗУ с перенесенными в него данными. |
||
5083 | Если эту директиву установить в FALSE, регистр DS все равно будет |
||
5084 | переустановлен на адрес сегмента ОЗУ, так как Ваш код будет использовать |
||
5085 | этот сегмент для неинициализированных глобальных переменных. |
||
5086 | Инициализированные переменные останутся в ПЗУ и все обращения к ним будут |
||
5087 | производиться через регистр CS. Так же останется не тронутым (таким, каким |
||
5088 | его установил главный BIOS) и стек. |
||
5089 | |||
5090 | 3. ?dataseg value - этой директивой компилятору сообщается сегментный |
||
5091 | адрес ОЗУ, который может быть использован вашим кодом. По умолчанию он |
||
5092 | равен 0x70. Этот адрес вы можете узнать в любой момент, считав его из вашего |
||
5093 | кода по смещению 4. Например: DS = CSWORD[4]; |
||
5094 | |||
5095 | Некоторые замечания: |
||
5096 | |||
5097 | 1. Не забывайте, что в момент инициализации ROM-BIOS, DOS еще не |
||
5098 | загружен, и соответственно все процедуры использующие вызовы DOS работать |
||
5099 | не будут. |
||
5100 | |||
5101 | 2. Нельзя завершать работу программы процедурами ABORT() или EXIT() и им |
||
5102 | подобным. Работа расширителя ROM-BIOS должна завершаться только выходом из |
||
5103 | процедуры main(). |
||
5104 | |||
5105 | 3. Если директива ?movedatarom установлена в FALSE, то будьте внимательны |
||
5106 | при работе с инициализированными переменными. Они в этом режиме доступны |
||
5107 | только для чтения, и адресуются через регистр CS. |
||
7544 | leency | 5108 | Return to contents. |
7496 | leency | 5109 | |
5110 | |||
7544 | leency | 5111 | |
7496 | leency | 5112 | 12.8 32-битные файлы. |
5113 | |||
5114 | 12.8.1 32-битный код под DOS. |
||
7544 | leency | 5115 | |
7496 | leency | 5116 | |
5117 | Для того чтобы откомпилировать 32-битную программу под DOS надо |
||
5118 | запустить компилятор с ключом командной строки /d32. Но работа 32-битной |
||
5119 | программы под DOS-ом невозможна без расширителя DOS. Для C-- можно |
||
5120 | использовать DOS4GW или zrdx.exe или любой другой расширитель DOS. Чтобы |
||
5121 | компилятор знал, где искать stub файл и его имя, надо в файл c--.ini |
||
5122 | прописать строку stub=path_name_to_stub_file. Пример: |
||
5123 | |||
5124 | stub=c:\c--\zrdx.exe |
||
5125 | |||
5126 | Если не добавлять в c--.ini эту строку, то компилятор сгенерирует |
||
5127 | 32-битный exe-файл, но без расширителя DOS. Если в командной строке |
||
5128 | вместе с ключом /d32 указать и ключ /ns, то строка с переменной stub из |
||
5129 | файла c--.ini будет аннулирована, и вы получите файл без расширителя DOS. |
||
5130 | |||
5131 | Для 32-битного DOS-файла можно использовать директивы компилятора |
||
5132 | ?parsecommandline TRUE/FALSE или его расширенный вариант ?argc |
||
5133 | TRUE/FALSE. Реализована и поддержка директивы ?atexit TRUE/FALSE. |
||
5134 | |||
5135 | Сейчас для 32-битных DOS-файлов используется LE-формат. Так как LE |
||
5136 | формат является стандартным, то теперь можно использовать почти любой |
||
5137 | stub, понимающий этот формат. Файлы LE формата можно сжимать программами |
||
5138 | типа UPX.EXE и ей подобными. |
||
5139 | |||
5140 | Если Вы используете stub, который затем загружает DOS4GW.EXE, то |
||
5141 | начало Вашей программы должно иметь специальную сигнатуру. Компилятор |
||
5142 | автоматически сформирует ее, если Вы в командной строке или в c--.ini |
||
5143 | файле укажете ключ /DOS4GW. Такой ключ Вам необходимо будет применять, |
||
5144 | если Вы будете использовать в качестве stub 4gs.exe. |
||
5145 | |||
5146 | Существует также поддержка блока кода использующего для перехода и |
||
5147 | работы в 32-битном режиме возможности DPMI сервиса. Исходный текст этого |
||
5148 | блока находится в файле startup.h-- и компилируется, если в командной |
||
5149 | строке указана опция /stub=dpmi или в файле c--.ini написать строку |
||
5150 | stub=dpmi. Недостатком этого способа перехода и работы в 32-битном |
||
5151 | режиме являются необходимость обязательного функционирования на |
||
5152 | запускаемом компьютере DPMI сервиса. Так как, программа загружается как |
||
5153 | обычная DOS программа, и лишь в процессе работы переходит в 32-битный |
||
5154 | режим работы, размер программы ограничен размером свободной DOS памяти. |
||
5155 | Ну а преимуществом его является компактный размер исполняемого файла. |
||
7544 | leency | 5156 | Return to contents. |
7496 | leency | 5157 | |
5158 | |||
7544 | leency | 5159 | |
7496 | leency | 5160 | 12.8.2 32-битный код под Windows. |
7544 | leency | 5161 | |
7496 | leency | 5162 | |
5163 | Для того чтобы откомпилировать программу, написанную под Windows надо |
||
5164 | запустить компилятор с ключом командной строки /w32. |
||
5165 | |||
5166 | Если Вы в своей программе используете вызовы API-процедур, то эти |
||
5167 | процедуры надо предварительно обязательно объявить. Объявление процедур |
||
5168 | имеет следующую форму: |
||
5169 | |||
5170 | extern WINAPI "DLL_name" |
||
5171 | { |
||
5172 | returncode procname1(); |
||
5173 | returncode procname2(); |
||
5174 | procname3(); |
||
5175 | } |
||
5176 | |||
5177 | где: |
||
5178 | DLL_name - имя и расширение dll-библиотеки, в которой находятся эти |
||
5179 | процедуры. |
||
5180 | returncode - тип возврата из api-процедур. По умолчанию он равен dword. |
||
5181 | |||
5182 | Программы, написанные под Windows, имеют одну немаловажную |
||
5183 | особенность - все параметры в стековые процедуры передаются в обратном |
||
5184 | порядке (так называемый C-стиль), но очистка стека от параметров |
||
5185 | происходит в самих процедурах. Получается своеобразный гибрид C и pascal |
||
5186 | стилей - stdcall. |
||
5187 | |||
5188 | С помощю ключа /W32C компилятор создает консольный файл под Windows. |
||
5189 | |||
7544 | leency | 5190 | Если при компиляции указывали опцию командной строки /j0 или |
7496 | leency | 5191 | директиву #jumptomain NONE, то Ваша программа будет компилироваться без |
5192 | использования кода начальной инициализации, описание которого находится в |
||
5193 | файле startup.h--. |
||
5194 | |||
7544 | leency | 5195 | Код начальной инициализации для программ под Windows имеет следующий |
7496 | leency | 5196 | вид: |
5197 | |||
7544 | leency | 5198 | hThisInst=GetModuleHandleA(0); |
7496 | leency | 5199 | #ifdef __CONSOLE__ |
7544 | leency | 5200 | hStdOut=GetStdHandle(-11); |
7496 | leency | 5201 | #endif |
5202 | lpszArgs=GetCommandLineA(); |
||
5203 | #ifdef __environ; |
||
7544 | leency | 5204 | environ=GetEnvironmentStringsA(); |
7496 | leency | 5205 | #endif |
7544 | leency | 5206 | main(); |
5207 | ExitProcess(EAX); |
||
7496 | leency | 5208 | |
7544 | leency | 5209 | Таким образом, в глобальных переменных hThisInst будет находится |
7496 | leency | 5210 | handl запущенного файла, а в lpszArgs адрес командной строки Вашего |
5211 | файла. Если Вы в командной строке указали опции /p или /argc или в |
||
5212 | начале вашего файла есть директивы #parsecommandline TRUE или argc TRUE, |
||
7544 | leency | 5213 | то компилятор создаст дополнительный код сделающий разборку этой |
7496 | leency | 5214 | командной строки на части. Если Вы компилируете консольную программу, то |
5215 | в вашей программе будет еще одна глобальная переменная - hStdOut. В этой |
||
5216 | переменной хранится handl стандартного вывода (экрана). Если Вы при |
||
5217 | компиляции программы указали опцию /env, то в глобальной переменной |
||
5218 | environ хранится адрес переменной окружения программы. |
||
5219 | |||
7544 | leency | 5220 | После завершения работы процедуры main выполнятся процедура |
7496 | leency | 5221 | ExitProcess, которой в качестве параметра передается регистр EAX. Т.о. |
5222 | Вам для завершения работы программы будет достаточно сделать выход из |
||
5223 | процедуры main, предварительно загрузив в регистр EAX нужный Вам код |
||
5224 | возврата. |
||
5225 | |||
5226 | Некоторые компиляторы создают DLL, в которых имена экспортируемых |
||
5227 | процедур имеют такой формат: |
||
5228 | |||
5229 | ProcName@8 |
||
5230 | |||
5231 | В этом имени после символа @ указывается размер стека с |
||
5232 | параметрами, передаваемых процедуре. |
||
5233 | |||
5234 | Объявлять такие процедуры нужно так: |
||
5235 | |||
5236 | extern WINAPI "name.dll" |
||
5237 | { |
||
5238 | ProcName@8 ; |
||
5239 | } |
||
5240 | |||
5241 | т.е. без круглых скобок. В программе, при обращении к такой процедуре, ее |
||
5242 | имя надо писать без суффикса @8, т.е. вот так - ProcName(param1,param2); |
||
7544 | leency | 5243 | Return to contents. |
7496 | leency | 5244 | |
5245 | |||
7544 | leency | 5246 | |
7496 | leency | 5247 | 12.8.3 Вызов API процедур по ординалам. |
7544 | leency | 5248 | |
7496 | leency | 5249 | |
5250 | В динамически подключаемых библиотеках (DLL) каждой процедуре, кроме |
||
5251 | ее имени, соответствует уникальное число, которое называется ординалом. И |
||
5252 | поэтому, кроме общепринятого вызова API-процедуры по имени, можно делать |
||
5253 | вызов и по ординалу. Теоретически, при использовании вызова по ординалу, |
||
5254 | загрузка файла должна происходить быстрее. Так как в выходной файл не |
||
5255 | будут включены списки имен процедур, вызов которых производится по |
||
5256 | ординалам, то выходной файл может получиться немного меньшим по размеру. |
||
5257 | |||
5258 | Чтобы компилятор создал файл, использующий вызов API-процедур по |
||
5259 | ординалам, надо сделать две вещи: |
||
5260 | |||
5261 | 1. Разрешить компилятору это делать. Для этого надо в опциях командной |
||
5262 | строки (или в файле C--.INI) указать ключ WO. |
||
5263 | |||
5264 | 2. Сообщить компилятору - какой номер ординала соответствует какому |
||
5265 | имени процедуры. Процедуры, для которых не был указан ординал, будет |
||
5266 | создан вызов по имени. Установить соответствие имен процедур ординалу |
||
5267 | можно двумя способами: |
||
5268 | |||
5269 | a). Автоматически, с помощью опции командной строки IND=name.dll, |
||
7544 | leency | 5270 | по которой компилятор просканирует эту библиотеку и импортирует из |
5271 | нее все имена и ординалы процедур. (Импорт возможет только из |
||
5272 | библиотек имеющих формат PE). |
||
7496 | leency | 5273 | |
5274 | b). В ручную указать в объявлении API-процедур и ее ординал. Делается |
||
7544 | leency | 5275 | это так: после имени процедуры ставится точка, а за ней указывается |
5276 | номер ординала. Вот пример объявления API-процедуры с указанием ее |
||
5277 | ординала: |
||
7496 | leency | 5278 | |
5279 | extern WINAPI "user32.dll" |
||
5280 | { |
||
5281 | ............ |
||
5282 | long MessageBoxA.429(); |
||
5283 | ............ |
||
5284 | } |
||
5285 | |||
5286 | В библиотеках (DLL), иногда существуют процедуры, для которых не |
||
5287 | указано их имя, но указан номер ординала. Вызов таких процедур по имени |
||
5288 | не возможен, но можно это сделать по ординалу (если, конечно Вы знаете, |
||
5289 | для чего эта процедура и что она делает). Для этого в объявлении |
||
5290 | API-процедуры Вам надо придумать для этой процедуры уникальное имя и |
||
5291 | указать реальный ординал. Затем в программе Вы будете обращаться к этой |
||
5292 | процедуре по вымышленному имени. Но если Вы случайно откомпилируете такой |
||
5293 | файл без ключа WO, то при запуске этой программы Вы получите сообщение, |
||
5294 | о том, что данного имени в библиотеке нет. |
||
5295 | |||
5296 | К сожалению, нет никаких гарантий того, что номер ординала для данной |
||
5297 | процедуры не изменится при смене версии динамической библиотеки. Поэтому |
||
5298 | использовать ординалы надо осторожно. |
||
7544 | leency | 5299 | Return to contents. |
7496 | leency | 5300 | |
5301 | |||
7544 | leency | 5302 | |
7496 | leency | 5303 | 12.8.4 Создание DLL под Windows. |
7544 | leency | 5304 | |
7496 | leency | 5305 | |
5306 | Динамически подключаемые библиотеки позволят получать более |
||
5307 | компактные программы и ускорить процесс компиляции. К минусам |
||
5308 | использования DLL можно отнести необходимость наличия самих файлов DLL на |
||
5309 | запускаемом компьютере и немного увеличивается время запуска программы. |
||
5310 | |||
5311 | Для того чтобы процедура стала доступной для других программ надо в |
||
5312 | исходном тексте перед именем процедуры прописать ключевое слово - _export. |
||
5313 | Пример: |
||
5314 | |||
5315 | void _export testproc() |
||
5316 | { |
||
5317 | .... |
||
5318 | } |
||
5319 | |||
5320 | Для того чтобы создать DLL, нужно написать файл, в котором будут |
||
5321 | процедуры с ключевыми словами _export. Вспомогательные процедуры, которые |
||
5322 | могут понадобиться для работы основных экспортируемых процедур, объявлять |
||
5323 | как _export необязательно. Затем этот файл нужно откомпилировать с ключом |
||
5324 | /dll. В результате Вы получите готовую динамически подключаемую |
||
5325 | библиотеку. |
||
7544 | leency | 5326 | Return to contents. |
7496 | leency | 5327 | |
5328 | |||
7544 | leency | 5329 | |
7496 | leency | 5330 | 12.8.5 Инициализация DLL при загрузке. |
7544 | leency | 5331 | |
7496 | leency | 5332 | |
5333 | Иногда, для работы процедур из динамических библиотек (DLL), бывает |
||
5334 | необходимым инициализировать некоторые переменные значениями, зависящими |
||
5335 | от текущего состояния операционной системы, например, получить дескриптор |
||
5336 | этой библиотеки. |
||
5337 | |||
5338 | Директивой #jumptomain NONE (-j0) управление при запуске передается |
||
5339 | сразу на процедуру main. |
||
5340 | |||
5341 | Во всех остальных случаях генерируется код заглушки и управление на |
||
5342 | процедуру main не передается. Фактически процедура main в этом случае не |
||
5343 | нужна. |
||
5344 | |||
5345 | Процедура main при создании файлов DLL должна выглядеть немного иначе, |
||
5346 | чем в других случаях: |
||
5347 | |||
5348 | dword main ( dword hInstDLL, reason, reserv ) |
||
5349 | { |
||
5350 | ... |
||
5351 | } |
||
7544 | leency | 5352 | Return to contents. |
7496 | leency | 5353 | |
5354 | |||
7544 | leency | 5355 | |
7496 | leency | 5356 | 12.8.6 Компиляция ресурсов. |
7544 | leency | 5357 | |
7496 | leency | 5358 | |
5359 | Встроенный в C-- компилятор ресурсов по своим возможностям уступает |
||
5360 | специализированным компиляторам ресурсов, но этих возможностей, как мне |
||
5361 | кажется, будет достаточно для большинства Ваших задач. |
||
5362 | |||
5363 | Будет проще перечислить то, что встроенный в C-- компилятор ресурсов |
||
5364 | не умеет делать. Не обрабатываются операторы ресурсов: VERSION, |
||
5365 | VERSIONINFO и определяемые пользователем ресурсы. При необходимости, |
||
5366 | данные, вводимые с помощью этих операторов, можно ввести с помощью |
||
5367 | оператора RCDATA. У многих операторов ресурсов есть необязательные |
||
5368 | параметры loading и 'memory'. Поддержка этих параметров не |
||
5369 | реализована. Встретив эти параметры, компилятор их просто пропустит. |
||
5370 | |||
5371 | Заставить компилятор C-- обрабатывать ресурсы можно двумя способами: |
||
5372 | |||
5373 | 1. Включить в свой проект директивой #include файл с расширением |
||
5374 | .rc. Файлы с таким расширением компилятор считает файлом с ресурсами. |
||
5375 | Файл ресурсов необходимо включать в Ваш проект лишь после включения |
||
5376 | заголовочных файлов Windows. |
||
5377 | |||
5378 | 2. Ресурсы можно располагать в теле исходного текста программы в |
||
5379 | произвольном месте. Текст ресурсов должен начинаться с директивы #pragma |
||
5380 | resource start, а заканчиваться директивой #pragma resoutce end. |
||
5381 | Ресурсы могут быть разделенными на части и эти части можно располагать в |
||
5382 | любом удобном для Вас месте (глупо располагать ресурсы в блоке |
||
5383 | комментариев и потом удивляться, почему они не были откомпилированы). |
||
5384 | Компилятор соберет эти части и откомпилирует. |
||
5385 | |||
5386 | Имена операторов можно писать как большими, так и маленькими буквами, |
||
5387 | но имена идентификаторов чувствительны к регистру. В тексте ресурсов |
||
5388 | можно использовать директивы и комментарии. |
||
5389 | |||
5390 | Ничто не мешает Вам использовать компиляторы ресурсов от других |
||
5391 | языков. Главное, чтобы синтаксис файла ресурсов соответствовал выбранному |
||
5392 | компилятору. |
||
7544 | leency | 5393 | Return to contents. |
7496 | leency | 5394 | |
5395 | |||
7544 | leency | 5396 | |
7496 | leency | 5397 | 12.9 Выходные файлы для MeOS. |
7544 | leency | 5398 | |
7496 | leency | 5399 | |
5400 | Исполняемые файлы для операционной системы MenuetOS поддерживаются |
||
5401 | компилятором совсем недавно. Для того, чтобы откомпилировать файл для |
||
5402 | MenuetOS, нужно в опциях компилятору указать /meos. Вы получите файл без |
||
5403 | расширения, который потом можно будет выполнить в среде операционной |
||
5404 | системы MenuetOS. |
||
5405 | |||
5406 | Если при компиляции файла Вы не указывали опцию /j0 или не |
||
5407 | использовали директиву #jumptomain NONE, то компилятор будет использовать |
||
5408 | файл начальной инициализации startup.h--, в котором для операционной |
||
5409 | системы MenuetOS создан блок инициализации и завершения программы. |
||
5410 | Завершать выполнение таких программ можно просто выйдя из процедуры main. |
||
7544 | leency | 5411 | Return to contents. |
7496 | leency | 5412 | |
5413 | |||
7544 | leency | 5414 | |
7496 | leency | 5415 | 13. Приложения. |
5416 | |||
5417 | 13.1 Поиск включаемых файлов. |
||
7544 | leency | 5418 | |
7496 | leency | 5419 | |
5420 | Поиск включаемого в вашу программу файла, имя которого объявляется |
||
5421 | директивой include и заключено в двойные кавычки "", производится |
||
5422 | компилятором по такой схеме: |
||
5423 | |||
5424 | сначала делается попытка открыть файл в текущей директории. Если файла там |
||
5425 | нет, то далее делается попытка открыть файл в директории указанной |
||
5426 | директивой #includepath. Если директива не была задана или файла в этой |
||
5427 | директории не оказалось, то делается попытка открыть файл в директории |
||
5428 | указанной в командной строке командой /ip=path. Если эта команда не была |
||
5429 | задана или файла в указанной директории не оказалось, то делается попытка |
||
5430 | открыть файл в директории указанной в файле C--.INI командой ip=. Если эта |
||
5431 | команда не была задана или файла в указанной директории не оказалось, то |
||
5432 | делается попытка открыть файл в директории, на которую указывает переменная |
||
5433 | окружения C--. Если переменная окружения не была задана или файла в этой |
||
5434 | директории не оказалось, то делается последняя попытка открыть файл в |
||
5435 | директории, откуда был запущен компилятор. |
||
5436 | |||
5437 | Если имя включаемого файла заключено в угловые скобки < >, то поиск |
||
5438 | этого файла производится в противоположном направлении, за исключением |
||
5439 | того, что поиск в текущей директории не производится. |
||
5440 | |||
5441 | Для консольной версии компилятора имена главного модуля и включаемых |
||
5442 | файлов могут иметь длину более 8 символов. |
||
7544 | leency | 5443 | Return to contents. |
7496 | leency | 5444 | |
5445 | |||
7544 | leency | 5446 | |
7496 | leency | 5447 | 13.2 Регистры, которые должны быть сохранены. |
7544 | leency | 5448 | |
7496 | leency | 5449 | |
5450 | Регистры, которые должны сохраняться - BP, DI, SI, DS, SS, SP, CS и IP. |
||
5451 | |||
5452 | BP используется как указатель на локальные и параметрические |
||
5453 | переменные в стеке, что и требует его сохранения. |
||
5454 | |||
5455 | DI и SI сохранять не обязательно, если программист осознает |
||
5456 | последствия. DI и SI часто используются для индексации массивов, как |
||
5457 | например в формуле: |
||
5458 | |||
5459 | dog = firehydrant(1,red) + legs[DI]; |
||
5460 | |||
5461 | Если DI не сохранялся в процедуре firehydrant, значение, присвоенное |
||
5462 | переменной dog, скорее всего, будет неправильным, поскольку индекс для |
||
5463 | массива legs был изменен. В сущности, для точного согласования все |
||
5464 | процедуры должны иметь специальное указание в комментарии на то, что в них |
||
5465 | не сохраняется содержимое регистров DI и/или SI. |
||
5466 | |||
5467 | DS указывает на сегмент данных, и все операции с глобальными |
||
5468 | переменными пользуются этим значением. |
||
5469 | |||
5470 | SS хранит сегмент стека и должен сохраняться. SP указывает на текущую |
||
5471 | позицию в стеке и тоже должен сохраняться. |
||
5472 | |||
5473 | CS хранит сегмент кода программы. Все команды выбираются с |
||
5474 | использованием CS и IP, следовательно их значения должны сохраняться. IP, |
||
5475 | как известно, указатель адреса команды, и CS и IP непосредственно не могут |
||
5476 | изменяться в процессорах 8086, 8088, 80286, 80386, 80486,... |
||
7544 | leency | 5477 | Return to contents. |
7496 | leency | 5478 | |
5479 | |||
7544 | leency | 5480 | |
7496 | leency | 5481 | 13.3 C--.ini файл. |
7544 | leency | 5482 | |
7496 | leency | 5483 | |
5484 | C--.ini файл предназначен для предустановки по умолчанию параметров |
||
5485 | компилятора. |
||
5486 | |||
5487 | Сейчас компилятор поддерживает огромное число параметров командной |
||
5488 | строки. Правильное их использование позволит Вам получать более компактный |
||
5489 | код и может значительно облегчить Вам отладку программы. Но так как этих |
||
5490 | параметров очень много набирать их каждый раз в командной строке бывает |
||
5491 | утомительно и не исключена возможность пропустить какой-нибудь параметр. |
||
5492 | Чтобы избавить Вас от всех этих напастей и был введен c--.ini файл. |
||
5493 | |||
5494 | Параметры командной строки прописываются в этом файле построчно. |
||
5495 | Синтаксис тот же, что и в командной строке, но без ведущего обратного слэша |
||
5496 | или минуса. Если файл расположен в директории, на которую указывает |
||
5497 | переменная окружения set c--= |
||
5498 | то в той же директории где и файл c--.exe, то эти параметры |
||
5499 | распространяются на все компилируемые программы. Если же файл c--.ini |
||
5500 | расположен в текущей директории, то параметры считываются только из этого |
||
5501 | файла и действуют только для текущего проекта. |
||
5502 | |||
5503 | Допустимо использование комментариев. Признаком начала комментария |
||
5504 | является символ ;. Все последующие символы после ; и до конца строки |
||
5505 | считаются комментарием. |
||
5506 | |||
5507 | Пример C--.ini файла: |
||
5508 | |||
5509 | r- |
||
5510 | X |
||
5511 | 3 ;это комментарий |
||
5512 | os |
||
5513 | |||
5514 | ini-файл может иметь любое имя (но расширение должно быть обязательно |
||
5515 | ini). Имя этого файла с расширением должно быть передано компилятору в |
||
5516 | командной строке. Файл c--.ini загружается и обрабатывается автоматически |
||
5517 | до загрузки файла указанного в командной строке. |
||
5518 | |||
5519 | Таким образом, файл *.ini можно использовать подобно make-файлу - в нем |
||
5520 | Вы можете указать и имя главного компилируемого модуля, и все необходимые |
||
5521 | для его компиляции настройки. |
||
5522 | |||
5523 | Как альтернативу c--.ini файлу, параметры командной строки можно |
||
5524 | прописывать непосредственно в начале главного файла компилируемого проекта, |
||
5525 | используя директиву pragma option. С одной стороны это обеспечит Вашему |
||
5526 | проекту независимость от настроек компилятора, если Ваш проект будет |
||
5527 | компилироваться на другом компьютере. Но с другой стороны некоторые |
||
5528 | настройки являются индивидуальными для данного компьютера (это расположение |
||
5529 | библиотек, имена и расположение stub-файлов). Какой вариант использовать |
||
5530 | решать Вам, но как говорят, и я с этим согласен, лучше пользоваться золотой |
||
5531 | серединой - Часть параметров прописать в c--.ini файле, а другую |
||
5532 | непосредственно в компилируемом файле. |
||
7544 | leency | 5533 | Return to contents. |
7496 | leency | 5534 | |
5535 | |||
7544 | leency | 5536 | |
7496 | leency | 5537 | 13.4 startup.h-- файл. |
7544 | leency | 5538 | |
7496 | leency | 5539 | |
5540 | В этом файле находятся исходные тексты, которые компилируются |
||
5541 | компилятором в код начальной инициализации файла, для всех поддерживаемых |
||
5542 | компилятором типов выходных файлов. Этот файл должен находится либо в |
||
5543 | директории вместе с компилятором, либо в директории с библиотечными файлами. |
||
5544 | Этот файл включается компилятором в проект автоматически, а включение его |
||
5545 | директивой include может привести к нежелательным результатам. |
||
5546 | |||
5547 | В блоке начальной инициализации программы может производится (если Вы |
||
5548 | это укажете с помощью опций командной строки или используя директивы), |
||
5549 | разбор командной строки на параметры, сохранение переменой окружения, |
||
5550 | поддержка работы процедуры ATEXIT, изменение размера доступной памяти для |
||
5551 | *.com файлов и многие другие подготовительные операции. Если Вы |
||
5552 | откомпилируете свой файл не используя никаких опций командной строки и у |
||
5553 | Вас будет отсутствовать c--.ini файл, а в самом компилируемом файле у Вас |
||
5554 | будут отсутствовать директивы, то при компиляции *.com файла в него будет |
||
5555 | включен блок изменяющий размер доступной памяти и сигнатура SPHINXC--. |
||
5556 | |||
5557 | Если Вы компилируете файл типа *.exe (кроме файла модели tiny для DOS) |
||
5558 | и используете директиву jumptomain NONE или ключ командной строки /j0, |
||
5559 | то для этого проекта файл startup.h-- компилятором не используется. Не |
||
5560 | используется этот файл также при компиляции *.com файлов если, кроме /j0, |
||
5561 | в этом проекте не используется разбор командной строки (/p /argc), не |
||
5562 | применяется процедура ATEXIT (/at), не используется адрес переменной |
||
5563 | окружения (/env), не используется очистка области post-адресов (/cpa), не |
||
5564 | используется уменьшение доступной программе памяти (/r) и не используется |
||
5565 | заглушка нажатий CTRL-C (/c). |
||
5566 | |||
5567 | Кроме блока начальной инициализации программы в файле startup.h-- |
||
5568 | находятся динамические процедуры: |
||
5569 | |||
5570 | void CLEARPOSTAREA( (E)AX ); - очистка post-области данных. |
||
5571 | unsigned int PARAMSTR( ECX ); - получить адрес элемента командной строки |
||
5572 | unsigned int PARAMCOUNT(); - получить число элементов в командной строке |
||
5573 | |||
5574 | При разборе командной строки на составляющие ее элементы для 32-битных |
||
5575 | программ реализована поддержка длинных имен. Для 16-битных программ |
||
5576 | поддержка разбора командной строки с учетом длинных имен подключается, если |
||
5577 | Вы в начале свой программы укажете директиву: |
||
5578 | |||
5579 | #define _USELONGNAME TRUE |
||
5580 | |||
5581 | либо в c--.ini файле или в командной строке компилятора укажете опцию |
||
5582 | d=_USELONGNAME. |
||
7544 | leency | 5583 | Return to contents. |
7496 | leency | 5584 | |
5585 | |||
7544 | leency | 5586 | |
7496 | leency | 5587 | 13.5 mainlib.ldp файл. |
7544 | leency | 5588 | |
7496 | leency | 5589 | |
5590 | В этом файле находится большое число процедур из основной библиотеки |
||
5591 | компилятора в уже откомпилированном виде. Все процедуры откомпилированы в |
||
5592 | 4-х различных режимах оптимизации. В этот файл также вынесены многие |
||
5593 | процедуры, которые ранее были внутри компилятора. Использование ранее |
||
5594 | откомпилированных процедур повышает скорость компиляции. |
||
5595 | |||
5596 | Эти процедуры откомпилированы только для 16-битного режима работы |
||
5597 | программы. Если Вы будете использовать эти процедуры в 32-битной программе, |
||
5598 | то компилятор на это не выдаст никаких сообщений и включит эту процедуру в |
||
5599 | Ваш код. Но при запуске такой программы она неизбежно потерпит крах. |
||
5600 | |||
5601 | Использовать эту библиотеку очень просто. Все что нужно, это |
||
5602 | расположить эту библиотеку в одной с компилятором директории. Тогда |
||
5603 | компилятор, если встретит в вашей программе вызов процедуры, которая не |
||
5604 | была определена ни во включаемых в программу библиотечных файлах, ни в |
||
5605 | вашей программе, будет искать эту процедуру в файле mainlib.ldp. Если эта |
||
5606 | процедура будет найдена в этом файле, то ее код будет перенесен в Ваш файл, |
||
5607 | иначе будет выдано сообщение о неизвестной процедуре. Таким образом, чтобы |
||
5608 | процедура была вставлена в вашу программу из библиотеки mainlib.ldp Вам |
||
5609 | нужно в свою программу не включать библиотечный файл, содержащий процедуру с |
||
5610 | таким же именем. |
||
5611 | |||
5612 | Список процедур находящихся в этой библиотеке можно получить с помощью |
||
5613 | специальной программы cmmlib.exe. Эту программу можно найти в архиве |
||
5614 | cmmlib.rar. Извлеките программу cmmlib.exe из этого архива и расположите ее |
||
5615 | в одной с компилятором директории. Затем запустите эту программу с ключом |
||
5616 | /L и Вы получите список процедур находящихся в этой библиотеке. |
||
7544 | leency | 5617 | Return to contents. |
7496 | leency | 5618 | |
5619 | |||
7544 | leency | 5620 | |
7496 | leency | 5621 | 13.6 C-- символы. |
7544 | leency | 5622 | |
7496 | leency | 5623 | |
5624 | SYMBOL|FUNCTION |EXAMPLE |
||
5625 | -------------------------------------------------------------------- |
||
5626 | /* |начинают блок комментария |/* комментарий */ |
||
5627 | */ |завершают блок комментария|/* комментарий */ |
||
5628 | | | |
||
5629 | // |комментарий до конца линии|// комментарий |
||
5630 | | | |
||
5631 | = |присвоение |AX = 12; |
||
5632 | + |сложение |AX = BX + 12; |
||
5633 | - |вычитание |house = dog - church; |
||
5634 | * |умножение или указатель |x = y * z; AL = * var; |
||
5635 | / |деление |x1 = dog / legs; |
||
5636 | & |поразрядное логическое И |polution = stupid & pointless; |
||
5637 | | |поразрядное логическое ИЛИ|yes = i | mabe; |
||
5638 | ^ |поразрядн. исключающее ИЛИ|snap = got ^ power; |
||
5639 | << |битовый сдвиг влево |x = y << z; |
||
5640 | >> |битовый сдвиг вправо |x = y >> z; |
||
5641 | | | |
||
5642 | += |сложение |fox += 12; // fox = fox +12; |
||
5643 | -= |вычитание |cow -= BX; // cow = cow - BX; |
||
5644 | *= |умножение |a *= b; // a = a * b; |
||
5645 | /= |деление |a /= b; // a = a / b; |
||
5646 | &= |поразрядное логическое И |p &= q; // p = p & q; |
||
5647 | |= |поразрядное логическое ИЛИ|p |= z; // p = p | z; |
||
5648 | ^= |поразрядн. исключающее ИЛИ|u ^= s; // u = u ^ s; |
||
5649 | <<= |битовый сдвиг влево |x <<= z; // x = x << z |
||
5650 | >>= |битовый сдвиг вправо |x >>= z; // x = x >> z |
||
5651 | | | |
||
5652 | >< |обмен значениями |x >< y; /* меняет местами значения x и y */ |
||
5653 | | | |
||
5654 | == |проверка на равенство |IF(AX == 12) |
||
5655 | > |проверка на больше чем |IF(junk > BOGUS) |
||
5656 | < |проверка на меньше чем |if( x < y ) |
||
5657 | >= |проверка больше или равно |if(AX >= 12) |
||
5658 | <= |проверка меньше или равно |IF(BL >= CH) |
||
5659 | != |проверка на неравенство |IF(girl != boy) |
||
5660 | <> |проверка на отличие |if (cat<>dog) /* та же функция что != */ |
||
5661 | | | |
||
5662 | @ |вставка кода |@ COLDBOOT(); /* вставляет COLDBOOT код */ |
||
5663 | : |динамическая процедура |: functionname () //объявляет functionname |
||
5664 | $ |ассемблерная команда |$ PUSH AX /* заносит AX в стек */ |
||
5665 | # |получение адреса(смещения)|loc = #cow; /* loc = address of cow */ |
||
5666 | |или директива | #resize FALSE |
||
5667 | ! |оператор NOT или смена |!x_var; if(!proc()) |
||
5668 | |флага операции сравнения. | |
||
5669 | ... |любое число параметров в | void proc(...); |
||
5670 | :: |разрешение видимости | ::var=0; |
||
7544 | leency | 5671 | Return to contents. |
5672 | |||
5673 | |||
7496 | leency | 5674 | |
7544 | leency | 5675 | |
7496 | leency | 5676 | >>=>>>>>><>=><=>=><=>><>><>>величина>текстовая>=MAX_X))>=MAX_X))>=MAX_X))>=MAX_X))>=MAX_X))>>>имя>>>>>10;BX++){ |