Add full compiler toolchain, libc, examples and reference docs
First substantive commit: the entire Sprinter C compiler tree on top of
the bare README+gitignore initial commit.
What's in here:
bin/sprinter-cc — driver script invoking SDCC + linker + mkexe
libc/ — Sprinter-specific libc layer over ESTEX/BIOS
(conio, gfx, io, mem, stdio + headers)
runtime/ — crt0 variants (default/small/banked/minimal)
+ heap + bank trampolines
toolchain/ — mkexe (SprintEXE packer, C + tests)
examples/ — 30 demo programs (gfx, file I/O, env, time, …)
lib/Makefile — builds the libc archive (sprinter.lib)
docs/ — converted Sprinter manuals + asm reference samples
third_party/ — solid-c reference compiler dump + sdcc setup script
release_docs/ — packaging / release notes
gitignore overhaul:
• Drop dangerous blanket patterns: *.asm (would hide docs/samples/*.asm)
and *.exe (case-insensitive match was hiding third_party/solid-c/*.EXE
on macOS APFS). Replaced with examples/*/*.{asm,exe,…} and lib/*.lib.
• Restore tracking of toolchain/mkexe/tests/{one,big}.bin — those are
INPUT fixtures, not build outputs.
• Collapse the duplicated SDCC/C/Sdcc sections into one section per
concern (build outputs / vendored / OS-junk).
• Add .sprinter-cc-*/, build/ (catches lib/build/ too), .claude/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
#include <stdio.h>
|
||||
|
||||
char buffer[4]="AAA";
|
||||
//char buffer[4]={'A','A','A','A'};
|
||||
|
||||
void main()
|
||||
{
|
||||
printf("0x%02X\n", *buffer); // (1) не правильно!
|
||||
printf("0x%02X\n", (char *) *buffer); // (2) правильно
|
||||
}
|
||||
|
||||
Если массив buffer представляет собой char-элементы, то при обращении к нему,
|
||||
как показано в строке (1), компилятор сгенерирует не тот код, что нам нужно.
|
||||
При этом, никакой ошибки выдано не будет. Для того, чтобы компилятор сгенери-
|
||||
ровал правильный код, к отдельному элементу char-массива нужно обращаться так,
|
||||
как это сделано в строке (2), принудительно указав компилятору через cast-
|
||||
операцию то, что нам нужно.
|
||||
При обращении к массиву, состоящему из int-элементов, вышеописанных проблем
|
||||
не возникает, компилятор генерирует правильный код.
|
||||
Для более наглядного представления, о чем идет речь, можно просмотреть asm-
|
||||
листинг, генерируемый компилятором для приведенного выше примера. Для строки
|
||||
(1) компилятор не сгенерирует код "ld b,0".
|
||||
Vendored
+968
@@ -0,0 +1,968 @@
|
||||
Компилятор языка Си в системе ESTEX
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
ВВЕДЕНИЕ
|
||||
|
||||
Одной из целей разработки оптимизирующего компилятора SOLID C было создание
|
||||
инструментального средства, которое позволяет писать и саму операционную сис-
|
||||
тему, и программное обеспечение для нее. Для достижения этой цели в кодогене-
|
||||
ратор введены различные дополнительные средства. Так, разработаны средства
|
||||
автоматического распределения регистров, благодаря которым удалось существенно
|
||||
повысить эффективность объектного кода. В результате стало возможным писать
|
||||
на высоко-уровневом языке (которым является Си) даже системы, работающие в
|
||||
условиях жестких ограничений по памяти и реальному масштабу времени. При этом
|
||||
почти полностью устранена необходимость программирования на Ассемблере.
|
||||
|
||||
Основная цель данного руководства - дать пользователям представление о
|
||||
применении компилятора в рамках операционной системы ESTEX и некоторых
|
||||
особенностях этого компилятора.
|
||||
|
||||
SOLID C является двухпроходным компилятором языка, преобразующим исходный
|
||||
си-текст в программу на языке Ассемблера. Компилятор состоит из двух файлов
|
||||
"cc1.exe" и "cc2.exe", выполняющих соответственно первый и второй проходы.
|
||||
На первом проходе входной си-текст преобразуется в объектный код (т-код),
|
||||
сохраняемый в файле с расширением ".tmc" (по-умолчанию). На первом проходе
|
||||
производятся также диагностика ошибок и выдача предупреждающих сообщений.
|
||||
На втором проходе полученный т-код преобразуется в ассемблерный листинг,
|
||||
сохраняемый в файле с расширением ".asm" (по-умолчанию).
|
||||
Следует отметить тот факт, что поскольку данный компилятор является оптимизи-
|
||||
рующим, то время, затраченное на компиляцию си-программы возрастает от 2-х и
|
||||
более раз, по сравнению с обычными (не оптимизирующими) си-компиляторами.
|
||||
|
||||
|
||||
ГЕНЕРАТОР ОБЪЕКТНОГО КОДА
|
||||
|
||||
Этот компонент (cc1.exe) компилятора обеспечивает первый проход. Он произ-
|
||||
водит разбор исходного си-текста, его контроль на допустимость синтаксиса
|
||||
и семантики. В результате первого прохода появляется промежуточный файл с
|
||||
объектным кодом, т.н. Т-код. Стандартным расширением имен файлов, содержащих
|
||||
т-код, является ".tmc".
|
||||
Команда запуска компилятора первого прохода имеет следующий формат:
|
||||
|
||||
CC1 [опция] имя_файла
|
||||
|
||||
Имя файла может содержать букву диска и путь, при этом максимальная длина
|
||||
строки не может превышать 80 символов. Обычно для исходных си-файлов исполь-
|
||||
зуется расширение ".c". Если расширение входного файла ".c", то его можно
|
||||
не указывать.
|
||||
|
||||
Опции генератора объектного кода
|
||||
|
||||
-c В отличие от стандартного языка Си, компилятор SOLID C
|
||||
обрабатывает гнездование (вложение) комментариев.
|
||||
Данная опция отключает режим гнездования комментариев.
|
||||
|
||||
-dNAME Определить макро-определение с именем "NAME". Между буквой опции
|
||||
и именем "NAME" пробелы не допускаются.
|
||||
|
||||
-e Режим переопределения выдачи сообщений в файл ошибок, имеющего
|
||||
расширение ".err". Файл создается всегда по текущему пути исходного
|
||||
".c" файла.
|
||||
|
||||
-jN Установка максимального числа ошибок компиляции. Происходит
|
||||
прерывание компиляции, если число ошибок равно или превышает
|
||||
"N". Между буквой опции и числом "N" пробелы не допускаются.
|
||||
Например: -j30 (прерывание процесса компиляции при 30 ошибках).
|
||||
|
||||
-k Режим совместимости со стандартным языком Си (стандарт K&R).
|
||||
Разрешает делать неявные объявления функций и параметров.
|
||||
Данную опцию необходимо использовать при компиляции "чужых"
|
||||
исходников.
|
||||
|
||||
-m Режим вывода на экран статистики по таблицам компилятора.
|
||||
|
||||
-oNAME Задать имя для выходного файла с т-кодом. Максимальная длина
|
||||
строки полного пути (диск\каталог\имя) файла составляет
|
||||
80 символов. Между буквой опции и именем "NAME" пробелы не
|
||||
допускаются.
|
||||
|
||||
-rP:S:H Установка отношения размеров частей рабочей области компилятора:
|
||||
P - пула, S - таблицы символов, H - хеш-таблицы.
|
||||
По-умолчанию используется соотношение 13:6:4. В случае переполнения
|
||||
какой-либо части, используйте эту опцию для назначения другого
|
||||
соотношения частей рабочей области.
|
||||
Между буквой опции и строкой "P:S:H" пробелы не допускаются.
|
||||
|
||||
-x Режим строгой совместимости с MSX-C компилятором. При этом
|
||||
расширение файла т-кода меняется с ".tmc" на ".tco".
|
||||
|
||||
|
||||
КОДОГЕНЕРАТОР
|
||||
|
||||
Кодогенератор (cc2.exe) выполняет второй проход компилятора. Он читает
|
||||
файл с т-кодом и создает выходной файл на языке Ассемблера.
|
||||
Команда запуска компилятора второго прохода имеет следующий формат:
|
||||
|
||||
CC2 [опция] имя_файла
|
||||
|
||||
Если у входного файла с т-кодом расширение не указано, то используется
|
||||
стандартное расширение ".tmc". Если не указано расширение для выходного
|
||||
файла - используется расширение ".asm". Максимальная длина строки
|
||||
"диск\каталог\имя_файла" должна быть не более 64 символов.
|
||||
Если путь расположения для выходного файла не задан, то по-умолчанию
|
||||
он создается по текущему пути расположения входного файла т-кода.
|
||||
|
||||
Опции кодогенератора
|
||||
|
||||
-k Не удалять входной файл т-кода (.tmc) по завершению работы
|
||||
кодогенератора.
|
||||
|
||||
-oNAME Задать имя для выходного asm-файла. Максимальная длина
|
||||
строки полного пути (диск\каталог\имя) файла составляет
|
||||
64 символа. Между буквой опции и именем "NAME" пробелы
|
||||
не допускаются.
|
||||
|
||||
-rN Резервирование области под таблицу символов компилятора размером
|
||||
N байтов (N - десятичный формат). Между буквой опции и числом "N"
|
||||
пробелы не допускаются. Например: -r2000 (резервируется таблица
|
||||
символов размером в 2000 байтов).
|
||||
|
||||
-uX Определить пост-символ "X" для имен в asm-листинге. По-умолчанию
|
||||
используется символ "_". Например: -u@ (использовать пост-символ
|
||||
"@" для имен в выходном asm-листинге).
|
||||
|
||||
-q "Тихий" режим. Индикаторы процесса компиляции "---" и "+++"
|
||||
на экран не выводятся.
|
||||
|
||||
|
||||
|
||||
|
||||
ОСОБЕННОСТИ КОМПИЛЯТОРА SOLID C ДЛЯ ОС ESTEX
|
||||
|
||||
|
||||
Как было сказано выше, целью разработки данного Си-компилятора было созда-
|
||||
ние продукта, с помощью которого можно было бы (пользуясь всеми преимуществами
|
||||
языка высокого уровня) разрабатывать программы, удовлетворяющие жестким требо-
|
||||
ваниям реального масштаба времени и ограничений на память. В данной главе опи-
|
||||
саны особенности компилятора с этой точки зрения. Кроме того, здесь будут даны
|
||||
рекомендации по программированию на SOLID C.
|
||||
|
||||
Компилятор SOLID C может работать в двух различных режимах. Один из них
|
||||
является "собственным" для данного компилятора, а второй - служит для обеспе-
|
||||
чения совместимости со стандартом языка Си. В этом режиме все арифметические
|
||||
преобразования выполняются точно так же, как в стандартном языке Си. Самое
|
||||
большое различие между этим режимом и "собственным" состоит в том, что в пос-
|
||||
леднем не выполняются автоматические преобразования форматов при переходах от
|
||||
типа "char" к типу "int". По-умолчанию, компилятор работает в "собственном"
|
||||
режиме. Для смены режима работы компилятора служит опция "-k" на первом прохо-
|
||||
де компилятора.
|
||||
|
||||
|
||||
Собственный режим компилятора
|
||||
|
||||
Для данного режима характерны два новых формата: целой константы (intconst)
|
||||
и символьной константы (charconst). Обычно указанные форматы используются иден-
|
||||
тично с соответствующими форматами "int" и "char", однако в случаях анализа
|
||||
типов операндов и выражений, и приведения их к соответствию, указанные два
|
||||
формата рассматриваются как различные.
|
||||
Как упоминалось выше, в этом режиме не производится автоматическое преобразо-
|
||||
вание форматов при переходе от типа "char" к "int". Действуют следующие согла-
|
||||
шения о преобразованиях типов:
|
||||
|
||||
unsigned + ? -> unsigned
|
||||
int + ? -> int
|
||||
char + ? -> char
|
||||
intconst + ? -> intconst
|
||||
charconst + ? -> charconst
|
||||
|
||||
|
||||
Режим совместимости со стандартом языка Си
|
||||
|
||||
Выполняются все арифметические преобразования, действительные в рамках реа-
|
||||
лизации стандартного языка Си. Единственное различие состоит в том, что компи-
|
||||
лятор SOLID C интерпретирует тип "char", как значение без знака.
|
||||
Для этого режима действуют следующие правила преобразования типов:
|
||||
|
||||
char -> int
|
||||
unsigned + ? -> unsigned
|
||||
int + int -> int
|
||||
|
||||
|
||||
|
||||
Рекурсивные функции
|
||||
|
||||
Как известно, все функции стандартного языка Си рекурсивны. В среде компи-
|
||||
лятора SOLID C можно писать также и нерекурсивные функции. Для различия
|
||||
функций этих двух типов введены и зарезервированы следующие ключевые слова:
|
||||
"recursive" - рекурсивная и "nonrec" - нерекурсивная. Эти ключевые слова
|
||||
записываются в заголовке функции перед ее именем и служат описателями режимов
|
||||
ее выполнения. Это показано в следующем примере:
|
||||
|
||||
nonrec main()
|
||||
{
|
||||
printf("Hello, world!\n");
|
||||
}
|
||||
|
||||
recursive
|
||||
int factorial(n)
|
||||
int n;
|
||||
{
|
||||
return(n > 1) ? n*factorial(n-1) : 1;
|
||||
}
|
||||
|
||||
int max(a,b)
|
||||
int a,b
|
||||
{
|
||||
return(a > b) ? a : b;
|
||||
}
|
||||
|
||||
В приведенном примере даны определения: нерекурсивной функции main() и
|
||||
рекурсивной функции factorial(). Для функции max() описатель режима выпол-
|
||||
нения функции в явном виде не задан, поэтому она будет выполняться так, как
|
||||
задано по-умолчанию. Обычно по-умолчанию задается режим выполнения функций
|
||||
как рекурсивных. Однако это умолчание можно изменять с помощью следующих
|
||||
предложений препроцессора:
|
||||
|
||||
#pragma nonrec
|
||||
#pragma recursive
|
||||
|
||||
Первым предложением по умолчанию задается режим выполнения функций, как
|
||||
нерекурсивных. Второе предложение предписывает рассматривать все функции,
|
||||
как рекурсивные (если явно не оговорено противоположное).
|
||||
Для случая, когда действует предложение #pragma, функция max() будет выпол-
|
||||
няться согласно последнему описателю режимов "рекурсивная/нерекурсивная".
|
||||
Рекомендуется давать явное указание "nonrec" кампилятору, если нужно, чтобы он
|
||||
воспринимал функции как нерекурсивные. По такому указанию, компилятор гаран-
|
||||
тированно сгенерирует объектный код, более короткий и более быстрый, чем в
|
||||
других случаях. В обычных прикладных программах бОльшая часть функций нерекур-
|
||||
сивна, поэтому в начале исходного текста следует писать предложения препроцес-
|
||||
сора: #pragma nonrec
|
||||
|
||||
|
||||
Функции с переменными параметрами
|
||||
|
||||
Компилятор SOLID C обрабатывает функции двух типов: с переменным числом
|
||||
параметров и с фиксированным. В функциях первого типа число и представление
|
||||
параметров могут изменяться, тогда как в функциях второго типа они фиксированы.
|
||||
Например, функции printf, scanf и ряд других являются функциями с переменным
|
||||
числом параметров. В компиляторе SOLID C функции указанных двух типов разли-
|
||||
чаются с помощью специальных описателей.
|
||||
Структура предложений языка изменяется с введением простых и абстрактных
|
||||
описателей следующим образом:
|
||||
|
||||
С простым описателем:
|
||||
идентификатор
|
||||
(описатель)
|
||||
*описатель
|
||||
описатель()
|
||||
описатель(.)
|
||||
описатель [константное выражение]
|
||||
|
||||
С абстрактным описателем:
|
||||
пусто
|
||||
абстрактный описатель
|
||||
*абстрактный описатель
|
||||
абстрактный описатель()
|
||||
абстрактный описатель(.)
|
||||
абстрактный описатель [константное выражение]
|
||||
|
||||
Конструкции "описатель(.)" и "абстрактный описатель(.)" впервые введены
|
||||
для обозначения функций с переменными параметрами. Конструкции "описатель()"
|
||||
и "абстрактный описатель()" обозначают функции с фиксированными параметрами.
|
||||
Данный метод описания функций с переменными параметрами используется в пред-
|
||||
ложениях типа cast, sizeof. Например, в следующих описаниях функции func и fp
|
||||
определены, как функции с переменными параметрами:
|
||||
|
||||
int func(.), (*fp)(.);
|
||||
|
||||
Перед применением функции с переменными параметрами, ее необходимо всякий раз
|
||||
описывать подобным образом. В библиотеке стандартных функций имеются функции
|
||||
с переменными параметрами, как например:
|
||||
|
||||
printf, fprintf, sprintf, scanf, fscanf, sscanf
|
||||
|
||||
Описания перечисленных функций содержатся в стандартном файле "stdio.h".
|
||||
Вызов функции с переменными параметрами совершенно идентичен вызову функции
|
||||
с фиксированными параметрами.
|
||||
Эти функции не обладают мобильностью с точки зрения рабочей машины (главным
|
||||
образом по методу доступа к параметрам). Отсутствие переносимости имеет место
|
||||
только для самой функции с переменными параметрами. Внешняя функция, ее вызы-
|
||||
вающая, является переносимой.
|
||||
Определение функции с переменными параметрами должно быть записано в самом
|
||||
начале, перед ее телом. Ниже приведено типичное определение функции func,
|
||||
являющейся функцией с переменными параметрами:
|
||||
|
||||
int func(.); /* это определение обязательно должно идти в начале */
|
||||
|
||||
int func(nargs, args)
|
||||
int nargs, args;
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
В момент вызова функции с переменными параметрами, вместо фактического
|
||||
параметра подставляется его копия - осуществляется вызов по значению.
|
||||
Значением параметра nargs является количество фактических параметров - фикси-
|
||||
рованная величина. Благодаря ей можно установить, сколько фактических парамет-
|
||||
ров реально передается в функцию с переменными параметрами со стороны вызыва-
|
||||
ющей функции. Однако возможности ограничены тем, что можно знать только число
|
||||
передаваемых параметров. При этом, установить тип каждого из этих параметров
|
||||
невозможно.
|
||||
Для того, чтобы организовать доступ к указанным параметрам, нужно описать
|
||||
каждый из передаваемых при вызове функции параметров. Первый из параметров
|
||||
(если он имеется) расположен по адресу массива args. Другими словами, значение
|
||||
первого из параметров args присваивается идентификатору args. Второй из пара-
|
||||
метров помещается по адресу, сдвинутому в сторону роста адресов памяти. Вообще
|
||||
можно составить последовательность адресов:
|
||||
|
||||
адрес 2-го параметра = адрес args + 2
|
||||
адрес 3-го параметра = адрес args + 4
|
||||
|
||||
В следующей таблице приведено соответствие параметров и их адресов:
|
||||
________________________________________________
|
||||
|
||||
Номер параметра Адрес
|
||||
________________________________________________
|
||||
|
||||
1 Адрес начала массива args
|
||||
2 Адрес начала + 2
|
||||
3 Адрес начала + 4
|
||||
...
|
||||
n Адрес начала + (n-1)*2
|
||||
________________________________________________
|
||||
|
||||
Согласно соответствию адресов и последовательности параметров, приведенному
|
||||
в таблице, можно организовать доступ к этим параметрам. Например, для того,
|
||||
чтобы сослаться на первый и второй параметры типа "char", необходимо написать
|
||||
в программе:
|
||||
|
||||
(char)args - ссылка на первый параметр типа "char"
|
||||
(char)+(&args+1) - ссылка на второй параметр типа "char"
|
||||
|
||||
Примечание: Если в приведенном выше примере args является "int", то благодаря
|
||||
описанию, значение &args преобразуется в тип "int". Адрес следующего по порядку
|
||||
аргумента формируется согласно формуле &args+1, в которой константа "1" преоб-
|
||||
разуется в соответствии с масштабом разрядной сетки конкретной машины: как пра-
|
||||
вило, это два байта (вообще, можно определить эту величину с помощью выражения
|
||||
sizeof(int*)). В конкретном случае это дает "адрес начала массива параметров
|
||||
плюс два". Аналогичным образом можно организовать доступ к параметрам других
|
||||
типов.
|
||||
В заключение данного параграфа приведены два примера простых функций с
|
||||
переменными параметрами: функция sum возвращает значение суммы переменного
|
||||
числа "int" параметров. Функция cmax возвращает максимальное значение из ряда
|
||||
"int" параметров в TINY (char), число которых - переменное.
|
||||
|
||||
#pragma nonrec
|
||||
typedef char TINY;
|
||||
|
||||
int sum(.);
|
||||
TINY cmax(.);
|
||||
|
||||
int sum(nargs, args)
|
||||
int nargs, args
|
||||
{
|
||||
int i, *p;
|
||||
for(i=0, p=&args; nargs --;)
|
||||
i += *p++;
|
||||
return i;
|
||||
}
|
||||
|
||||
TINY cmax(nargs, args)
|
||||
int nargs;
|
||||
TINY args;
|
||||
{
|
||||
int *p, max;
|
||||
for(max=0, p=(int*)&args; nargs--; p++)
|
||||
if(max < (TINY)*p)
|
||||
max = (TINY)*p;
|
||||
return max;
|
||||
}
|
||||
|
||||
|
||||
Преобразования байтовой арифметики
|
||||
|
||||
Для повышения эффективности объектного кода, компилятор SOLID C выполняет
|
||||
преобразования байтовой арифметики. Благодаря этой особенности, все преобразо-
|
||||
вания переменных, имеющих тип "char", выполняются не со словами, а с байтами.
|
||||
Поскольку процессор Z80 с восьмибитовым форматом команд выполняет эти операции
|
||||
значительно быстрее, нежели другие. Байтовая арифметика особенно эффективна
|
||||
при написании специальных программ, включающих байтовые преобразования.
|
||||
Результаты логических операций и операций отношения
|
||||
|
||||
&& || ! = != > < >= <=
|
||||
|
||||
имеют тип "char".
|
||||
Традиционно, те байтовые переменные, которые обозначают внешние кодовые
|
||||
символы, специфицируются с помощью определения типа typedef в виде TINY или
|
||||
BOOL. Первый определяет арифметические объекты, а второй - логические.
|
||||
Определения указанных типов включены в файл стандартных заголовков "types.h".
|
||||
|
||||
Ниже даны несколько примеров употребления байтовых операций, на которые
|
||||
следует обратить особое внимание!.
|
||||
|
||||
1). Переполнение и исчезновение
|
||||
|
||||
При выполнении байтовых арифметических операций, возможны случаи перепол-
|
||||
нения или исчезновения. Например, в процессе выполнения операторов
|
||||
|
||||
int i;
|
||||
TINY x,y;
|
||||
|
||||
x = 10; y = 30;
|
||||
i = x * y;
|
||||
|
||||
вместо правильного значения 300, переменная "i" получает неправильное зна-
|
||||
чение 44. Это происходит вследствие того, что в процессе выполнения умножения
|
||||
по правилам байтовой арифметики теряется старший байт результата. Для того,
|
||||
чтобы получить правильный результат (число 300), нужно оперировать с целыми
|
||||
операндами, для чего следует специфицировать их с помощью функции cast, как
|
||||
показано в следующем примере:
|
||||
|
||||
i = (int)x * (int)y;
|
||||
|
||||
Такое же явление возникает и в следующей ситуации:
|
||||
|
||||
i = x << 8 | y;
|
||||
|
||||
При выполнении операции сдвига над операндом байтовой длины, может быть
|
||||
потеряна его часть вследствие переполнения. На это также нужно обратить внима-
|
||||
ние. Величину "y" необходимо подставить в выражение для "i" в том же самом
|
||||
виде, что и показано ниже:
|
||||
|
||||
i = (unsigned)x << 8 | y;
|
||||
|
||||
2). У типа TINY (char) отсутсвует знак числа. Переменные типа "char" исполь-
|
||||
зуются без знака. Поэтому иногда возникают ситуации, в которых результат опе-
|
||||
рации вычитания становится отрицательным. Поэтому, если выполнить ниже-следую-
|
||||
щую программу, в переменную "i" будет записано 255:
|
||||
|
||||
int i;
|
||||
TINY x,y;
|
||||
|
||||
x = 2;
|
||||
y = 3;
|
||||
i = x - y;
|
||||
|
||||
Для того, чтобы получить правильный результат, нужно выполнить эту операцию
|
||||
в соответствии с типом "int":
|
||||
|
||||
i = (int)x - (int)y;
|
||||
|
||||
3). Согласование параметров
|
||||
|
||||
Компилятор SOLID C проводит четкое различие между типами "int" и "char".
|
||||
Поэтому фактические и формальные параметры должны быть обязательно согласованы
|
||||
между собой по типу. Следующий пример иллюстрирует неправильный вызов функции:
|
||||
|
||||
func(x)
|
||||
TINY x;
|
||||
{
|
||||
...
|
||||
}
|
||||
main()
|
||||
{
|
||||
func(1); /* неправильный вызов функции */
|
||||
}
|
||||
|
||||
Нужно изменить вызов следующим образом:
|
||||
|
||||
func((TINY)1);
|
||||
|
||||
Согласование параметров по типу, компилятором не проверяется. Поэтому,
|
||||
следует обращать особое внимание на подобные ситуации.
|
||||
|
||||
|
||||
Распределение регистров
|
||||
|
||||
Поскольку компилятор SOLID C поддерживает распределение регистровых пере-
|
||||
менных, он способен генерировать более эффективный объектный код. В стандарт-
|
||||
ном языке Си предусмотрено размещение непосредственно в регистрах (класс памя-
|
||||
ти "register") только нескольких первых переменных из всего их множества.
|
||||
(Какое именно количество регистровых переменных будет помещено в регистры,
|
||||
зависит от того, какая рабочая машина будет использоваться). В отличие от
|
||||
стандартного, компилятор SOLID C автоматически выполняет оптимальное распре-
|
||||
деление регистров, в соответствии с потоком данных. Этим самым снята необхо-
|
||||
димость обязательного указания переменных программы, как регистровых. Вопрос,
|
||||
какие из переменных будут помещены в регистры, решается на уровне кодогенера-
|
||||
ции. Поэтому описание "register" идентично пo смыслу описанию "auto" и никакого
|
||||
другого смысла не имеет.
|
||||
В каждой функции программы, первые 16 переменных простого типа становятся
|
||||
кандидатами на размещение в регистрах, как переменные типов "аuto" или
|
||||
"register". K числу таких простых типов относятся "int", "char", "unsigned"
|
||||
и "указатель".
|
||||
Средствами языка Си можно получать адреса переменных. Компилятор SOLID C
|
||||
также, безусловно, располагает этими средствами. Однако, в случаях тех перемен-
|
||||
ных, которые являются объектами распределения регистровой памяти, возникает
|
||||
проблема. Она обусловлена тем, что как таковой "адрес" регистра не существует.
|
||||
Например, если написать
|
||||
|
||||
int n;
|
||||
n = 10;
|
||||
scanf("%d", &n);
|
||||
printf("%d", n);
|
||||
|
||||
и ввести 100, то какое значение получит переменная "n" ?. Конечно, 100.
|
||||
Компилятор поместит значение переменной по заданному адресу в память, если
|
||||
указать ему значение этого адреса. Следовательно, никаких проблем не возникнет,
|
||||
если при вызове функции передать ей в качестве параметра адрес переменной.
|
||||
Однако в примере
|
||||
|
||||
int n;
|
||||
int *p;
|
||||
p = &n;
|
||||
n = 10;
|
||||
*p = 100;
|
||||
printf("%d", n);
|
||||
|
||||
возможна ситуация, когда в качестве значения переменной "n" будет индициро-
|
||||
вано не 100, а 10. Между двумя предложениями, одно из которых "n=10", a другое
|
||||
"printf("%d",n)", не фигурирует "&n". Поэтому компилятор не помещает "n" в па-
|
||||
мять.
|
||||
|
||||
|
||||
|
||||
НЕКОТОРЫЕ ДРУГИЕ ОСОБЕННОСТИ КОМПИЛЯТОРА
|
||||
|
||||
|
||||
Таблица представления данных различного типа
|
||||
-----------------------------------------------------
|
||||
Тип Разрядность Диапазон значений
|
||||
-----------------------------------------------------
|
||||
char 8 0..255
|
||||
short 16 -32768..32767
|
||||
int 16 -32768..32767
|
||||
unsigned 16 0..65535
|
||||
_____________________________________________________
|
||||
|
||||
|
||||
1. В рамках компилятора SOLID C проводится идентификация переменных и
|
||||
функций по первым 30-ти символам имени. Благодаря такому расширению имен,
|
||||
можно использовать легко поддающиеся анализу имена переменных и функций, и
|
||||
получать удобочитаемые тексты программ.
|
||||
|
||||
2. Позиция ошибки диагностируется по номеру ошибочной строки и начальному
|
||||
символу (колонки в строке). Начальная позиция считается с 0. Таким образом,
|
||||
позиция первого символа программного текста есть "нулевая строка, нулевая ко-
|
||||
лонка".
|
||||
|
||||
3. Для указания имени включаемого файла используется предложение "#include"
|
||||
препроцессора, как описано ниже:
|
||||
|
||||
<имя файла> - ссылка на файл, расположенный в подкаталоге "INCLUDE"
|
||||
каталога компилятора.
|
||||
"имя файла" - ссылка на файл, расположенный по текущему пути исходного
|
||||
".c" файла.
|
||||
|
||||
Задание пути в имени файла не допускается. Например, если имеются предложения
|
||||
|
||||
#include <stdio.h>
|
||||
#include "common.h"
|
||||
|
||||
то в первом случае сделана ссылка на стандартный файл компилятора Си, распо-
|
||||
ложенного в подкаталоге с зарезервированным именем "INCLUDE" каталога SOLID C
|
||||
(по-умолчанию "SOLID"), а во втором случае - на файл, расположенный по теку-
|
||||
щему пути входного ".c" файла.
|
||||
Следует обратить внимание на то, что имя подкаталога "INCLUDE" зарезервировано
|
||||
и его не следует менять.
|
||||
|
||||
4. Типы long, float, double не поддерживаются, но зарезервированы. Поэтому
|
||||
нельзя использовать слова "long", "float" и "double" в качестве идентифика-
|
||||
торов переменных и меток.
|
||||
|
||||
5. Не поддерживаются битовые поля структур.
|
||||
|
||||
6. Некоторые ограничения в константных выражениях. Нельзя включать в
|
||||
константные выражения указанных ниже типов унарную операцию "sizeof":
|
||||
a) сразу после оператора "case" (в качестве метки);
|
||||
b) в описателе массива в качестве его размерности.
|
||||
Подобные ограничения не действуют на те константные выражения, которые
|
||||
появляются в инициаторе (т.е. при инициализации).
|
||||
|
||||
7. Имеется отличие от стандартного языка Си по эффективным пределам имен
|
||||
членов структур и объединений (struct & union). В стандартном Си одно и то же
|
||||
имя члена может появляться в двух различных структурах только в случае, когда
|
||||
они оба одного типа и имеют одинаковое смещение в обеих структурах. Компилятор
|
||||
SOLID C воспринимает имя члена так, как принято в Паскале или в Аде. Таким об-
|
||||
разом, одно и то же имя члена может появляться в разных структурах и будет
|
||||
правильно обработано компилятором SOLID C.
|
||||
В качестве примера рассмотрим следующее описание:
|
||||
|
||||
struct node {
|
||||
char *word;
|
||||
int count;
|
||||
struct node *next;
|
||||
} pool[1000], *p;
|
||||
|
||||
struct noad {
|
||||
int atr;
|
||||
struct noad *next;
|
||||
} table[10], *q;
|
||||
|
||||
В стандартном языке Си данная запись является неправильной, потому, что имя
|
||||
члена "next" появляется в двух структурах - "node" и "noad", где их типы и сме-
|
||||
щения не согласованы. Однако в среде компилятора SOLID C эти записи являются
|
||||
правильными.
|
||||
Следует обратить внимание на следующую запись
|
||||
|
||||
p->next
|
||||
|
||||
которая описывает член типа "struct node" (потому что "p" есть указатель типа
|
||||
"struct node"). Благодаря такому приему можно подбирать правильные члены путем
|
||||
строгого указания их типов. Так, в операциях "." и "->" левые члены должны быть
|
||||
указателями структур, включающих члены, записанные в качестве правой части этих
|
||||
операций. Например:
|
||||
|
||||
i->count
|
||||
|
||||
включает "i" не как указатель типа "struct node", а как "int" (целое). Поэтому
|
||||
компилятор SOLID C такую ситуацию не диагностирует. Для того, чтобы получить
|
||||
такой же результат, необходимо переписать приведенное выражение следующим
|
||||
образом:
|
||||
|
||||
((struct node *)i)->count
|
||||
|
||||
В приведенном выше примере для того, чтобы операция "->" была более высокого
|
||||
ранга, нежели операция "cast", следует терм
|
||||
|
||||
(struct node *) i
|
||||
|
||||
заключить в скобки.
|
||||
До того, пока не будет преобразован тип указателя в "int", что выполняется
|
||||
с помощью функции cast, операция не может быть выполнена.
|
||||
Даже при выполнении арифметических операций в случае указателей разных ти-
|
||||
пов, также необходимо использовать функцию cast. Например, в случае описаний
|
||||
|
||||
int i;
|
||||
char *p;
|
||||
int *q;
|
||||
|
||||
операции
|
||||
|
||||
p = i;
|
||||
if(p == i)
|
||||
q = p;
|
||||
if(p == q)
|
||||
|
||||
будут отмечены как ошибочные.
|
||||
Две первые ошибочны потому, что производятся подстановки между типами указа-
|
||||
теля и "int" (целого), и операции сравнения между ними. Две последние также
|
||||
ошибочны, потому, что несмотря на то, что и "p", и "q" обе являются указате-
|
||||
лями, но они специфицированы разными типами и поэтому компилятор отметит их,
|
||||
как ошибочные.
|
||||
Эти ошибки можно устранить, либо использованием функции cast, либо указанием
|
||||
опции "-k" на первом проходе компилятора.
|
||||
С другой стороны, разрешены преобразования типов из целого в указатель и
|
||||
обратно. Это обусловлено тем, что в языке Си целая константа 0 используется
|
||||
как указатель особого адреса. Например, запись
|
||||
|
||||
char *p;
|
||||
if(p != 0)
|
||||
p = 0;
|
||||
|
||||
не содержит ошибки.
|
||||
Типы "int" и указателей нельзя смешивать в среде компилятора SOLID C. Дан-
|
||||
ное соглашение вообще может считаться показателем хорошего стиля программиро-
|
||||
вания, при котором отладка программы становится проще.
|
||||
|
||||
8. Нет автоматического преобразования между типами "указатель" и "int",
|
||||
и обратно. В стандартном языке Си разрешены операции сравнения значений указа-
|
||||
телей и целых (int), но в SOLID C смешивание указанных типов вызовет сообщение
|
||||
об ошибке. Однако этот режим можно разрешить, указав опцию компилятора "-k"
|
||||
(на первом проходе).
|
||||
|
||||
9. Тип "char" интерпретируется как значение без знака, т.е. может иметь
|
||||
значение 0..255.
|
||||
|
||||
10. В рамках стандартного языка Си, в случаях появления необъявленных ранее
|
||||
функций, они интерпретируются как возвращающие "int" значение. Параметры
|
||||
необъявленного типа, которые вошли в список параметров, считаются "int".
|
||||
Рассматриваемый здесь компилятор не позволяет использовать эти умолчания. То
|
||||
есть, хороший стиль программирования предполагает описание всех идентификато-
|
||||
ров до того, как на них будут выполняться ссылки - автоматические ссылки вы-
|
||||
полняться не будут.
|
||||
Все объявления, которые относятся к функциям стандартной библиотеки, запи-
|
||||
саны в файле стандартных заголовков, поэтому любую из стандартных функций можно
|
||||
использовать без объявления. Поэтому все пользовательские программы должны
|
||||
содержать ссылку на файл "stdio.h" в самом начале текста, в предложении
|
||||
#include <stdio.h>.
|
||||
Однако, если на первом проходе компилятора указать опцию "-k", указанные
|
||||
описания функций будут объявлены по-умолчанию. От этого теряется качество полу-
|
||||
чаемого кода, поэтому данной опцией целесообразно пользоваться, если важно
|
||||
обеспечить совместимость со стандартным языком Си.
|
||||
|
||||
11. В данной версии рассматриваемого компилятора не поддерживается предложе-
|
||||
ние препроцессора "#if". Вполне возможно заменить указанное предложение дру-
|
||||
гими: "#ifdef" или же "#ifndef". Возможны также другие варианты замены предло-
|
||||
жения "#if". Рассматриваемый здесь компилятор не генерирует код тех участков
|
||||
программы, выполнение которых не требуется при ее прогоне. Поэтому можно полу-
|
||||
чить точно такой же результат, если применить оператор "if" вместо предложе-
|
||||
ния "#if". Например, можно заменить текст одной из ниже-следующих программ на
|
||||
другую:
|
||||
|
||||
Программа 1:
|
||||
|
||||
#if sizeof(FCB) != sizeof(char[36])
|
||||
Open_MSX();
|
||||
#else
|
||||
Open_OTHER();
|
||||
#endif
|
||||
|
||||
Программа 2:
|
||||
|
||||
if (sizeof(FCB) != sizeof(char[36]))
|
||||
Open_MSX();
|
||||
else
|
||||
Open_OTHER();
|
||||
|
||||
12. Введена директива препроцессора "#pragma". Применяется для различных
|
||||
указаний компилятору. Все предложения данного вида могут появляться в любом
|
||||
месте исходной программы. Ниже, в качестве примеров приведены четыре предложе-
|
||||
ния с заданием соответствующих директив компилятору:
|
||||
|
||||
1. #pragma optimize time
|
||||
|
||||
Данное указание компилятор интерпретирует так, что он старается сгенерировать
|
||||
объектный код, отличающийся более высокой скоростью выполнения, нежели в слу-
|
||||
чае, когда он должен быть более компактным.
|
||||
|
||||
2. #pragma optimize size
|
||||
|
||||
При таком указании, компилятор считает более важным сгенерировать компактный
|
||||
объектный код, хотя бы и не очень быстрый в исполнении.
|
||||
|
||||
3. #pragma nonrec
|
||||
|
||||
Этой директивой изменяется задание по-умолчанию режима выполнения функций,
|
||||
как нерекурсивных.
|
||||
|
||||
4. #pragma recursive
|
||||
|
||||
С помощью данного предложения устанавливается по-умолчанию режим выполнения
|
||||
функций как рекурсивных.
|
||||
|
||||
13. Поскольку компилятор SOLID C поддерживает для переменных автоматическое
|
||||
распределение регистров, класс памяти всегда устанавливается "auto" (автомати-
|
||||
ческий).
|
||||
|
||||
14. Компилятор SOLID C позволяет гнездовать тексты комментариев. Эта функция
|
||||
обеспечивает возможность написания больших программ, содержащих комментарии, в
|
||||
теле которых, в свою очередь содержатся комментарии. Если на первом проходе
|
||||
указать компилятору опцию "-c", гнездование комментариев производиться не бу-
|
||||
дет, что отвечает возможностям стандартного языка Си.
|
||||
|
||||
|
||||
|
||||
ПЕРЕДАЧА ПАРАМЕТРОВ
|
||||
|
||||
Функции с фиксированными параметрами
|
||||
|
||||
В случае обычных функций (функций с фиксированными параметрами), первые три
|
||||
параметра помещаются в регистры, а все последующие - в стек. Если какой-либо
|
||||
из 3-х параметров имеет тип "char", он помещается в соответствующий 8-ми битный
|
||||
регистр. Параметры (первые три) других типов помещаются в регистровые пары.
|
||||
4-й параметр (если есть) и все последующие - помещаются в стек, в обратном по-
|
||||
рядке (паскалевский способ ?). На вершину стека помещается адрес возврата.
|
||||
Каждый из размещаемых в стеке параметров занимает два байта. Значение "char"
|
||||
параметра помещается в младший байт, а значение старшего байта остается неопре-
|
||||
деленным.
|
||||
В режиме совместимости со стандартным языком Си, использование параметра типа
|
||||
"char" имеет отличия: преобразование из "char" формата в "int" производится
|
||||
автоматически (при этом старший байт обнуляется), после чего данные рассматри-
|
||||
ваются как "другой тип".
|
||||
|
||||
_______________________________________________________________________
|
||||
|
||||
Тип параметра Первый Второй Третий Четвертый Пятый ...
|
||||
_______________________________________________________________________
|
||||
|
||||
char A E C (SP+2) (SP+4) ...
|
||||
|
||||
другой тип HL DE BC (SP+2) (SP+4) ...
|
||||
(SP+3) (SP+5) ...
|
||||
_______________________________________________________________________
|
||||
|
||||
|
||||
Функции с переменными параметрами
|
||||
|
||||
Когда вызывается функция с переменными параметрами, все параметры заталки-
|
||||
ваются в стек (в обратном порядке). Число параметров, помещенных в стек, запи-
|
||||
сывается в регистровую пару HL. На вершину стека помещается адрес возврата.
|
||||
В "собственном" режиме, "char" параметр помещается в младший байт, значение
|
||||
старшего байта остается неопределенным.
|
||||
В режиме совместимости со стандартом языка Си, "char" тип автоматически
|
||||
преобразуется в "int" тип (при этом старший байт обнуляется), который далее
|
||||
трактуется как "другой тип".
|
||||
Параметры других типов размещаются в стеке согласно правилам стандарта для
|
||||
процессора Z80, а именно: первым запоминается младший байт, вторым - старший.
|
||||
|
||||
_______________________________________________________________________
|
||||
|
||||
Тип параметра Количество Первый Второй Третий ...
|
||||
_______________________________________________________________________
|
||||
|
||||
char HL (SP+2) (SP+4) (SP+6) ...
|
||||
другой тип HL (SP+2) (SP+4) (SP+6) ...
|
||||
(SP+3) (SP+5) (SP+7)
|
||||
_______________________________________________________________________
|
||||
|
||||
|
||||
|
||||
ВОЗВРАТ ЗНАЧЕНИЯ ФУНКЦИИ
|
||||
|
||||
При передаче управления вызывающей программе, функция может передавать свое
|
||||
значение через регистры. При этом функция типа "char" передает значение через
|
||||
регистр "A". Функции других типов передают свои значения через регистровую
|
||||
пару HL.
|
||||
В режиме совместимости со стандартным языком Си, возвращаемое значение всегда
|
||||
передается через регистровую пару HL, даже в том случае, когда функция имеет
|
||||
"char" тип.
|
||||
Для возврата в вызывающую функцию двух и более значений, необходимо исполь-
|
||||
зовать указатели переменных, как параметры любых других си-программ.
|
||||
|
||||
|
||||
ПРАВИЛА ВЫЗОВА ФУНКЦИЙ
|
||||
|
||||
1. Во-первых, вызывающая программа оформляет параметры и помещает их в ре-
|
||||
гистры или заталкивает в стек согласно правилам, описанным выше.
|
||||
2. Программа вызывает функцию. Для вызова обычно используется слово "call".
|
||||
3. После возврата управления функцией, параметры выталкиваются из стека
|
||||
(с использованием команды "pop"). Если все параметры размещены в регистрах,
|
||||
использовать команду "pop" нет необходимости. Следует отметить, что в случае
|
||||
применения команды "pop", возвращаемые через регистры "A" или "HL" значения
|
||||
функций не будут потеряны.
|
||||
4. Значение функции возвращается через регистр "A", если функция имеет
|
||||
"char" тип. Для функций другого типа, значение всегда возвращается через
|
||||
регистровую пару HL.
|
||||
|
||||
|
||||
ПЕРЕДАЧИ УПРАВЛЕНИЯ ВЫЗЫВАЕМОЙ ФУНКЦИИ
|
||||
|
||||
Вызываемая функция не должна разрушить содержимое указателя стека. Иными
|
||||
словами, содержимое указателя стека при выходе из функции должно остаться тем
|
||||
же, что и при входе в нее. Параметры, загруженные в стек, должны выталкиваться
|
||||
из него вызывающей программой. Вызываемая функция может изменить значения па-
|
||||
раметров (как тех, которые передаются через регистры, так и загруженных в стек).
|
||||
Однако следует подчеркнуть, что в языке Си действует соглашение, в рамках кото-
|
||||
рого вызов функций осуществляется по значению, поэтому даже если будут возвра-
|
||||
щены измененные параметры, это никакого влияния на процесс вызова функции не
|
||||
окажет.
|
||||
Возвращаемое значение функции помещается в регистр "A" или в регистровую
|
||||
пару HL - в зависимости от типа вызванной функции. Эти правила описаны в пре-
|
||||
дыдущем разделе.
|
||||
|
||||
Пример 1:
|
||||
|
||||
В следующем примере объявлена ассемблерная функция ISDIGIT, которая возвра-
|
||||
щает значение функции BOOL в регистре "A". Вызывающая функция передает параметр
|
||||
"char" типа через регистр "A".
|
||||
|
||||
... вызывающая программа (на языке Си)...
|
||||
|
||||
typedef char BOOL; /* определение "char" типа как BOOL */
|
||||
...
|
||||
BOOL isdigit(c);
|
||||
char c;
|
||||
...
|
||||
if (isdigit(c))
|
||||
...
|
||||
...
|
||||
|
||||
... вызываемая функция (на языке Ассемблера)...
|
||||
|
||||
public isdigit_
|
||||
|
||||
isdigit_:cp '0'
|
||||
jr c,false
|
||||
cp '9'+1
|
||||
ret c ; цифра, вернуть true
|
||||
false: xor a ; не цифра, вернуть false
|
||||
ret
|
||||
|
||||
|
||||
Пример 2:
|
||||
|
||||
Этот пример соответствует случаю вызова функции, написанной на Си, из ас-
|
||||
семблерной процедуры.
|
||||
Стандартные библиотечные функции printf и puts вызываются из ассемблерной
|
||||
процедуры. Их исходные тексты находятся в исходниках библиотеки, поэтому здесь
|
||||
функции printf и puts не будут описаны. Следует заметить, что поскольку функ-
|
||||
ция printf - с переменными параметрами, ее параметры заталкиваются в стек.
|
||||
В данном примере параметр всего один, однако в случаях двух и большего числа
|
||||
параметров, они помещаются в стек в обратной последовательности. Число парамет-
|
||||
ров передается в вызываемую функцию через регистровую пару HL (в приведенном
|
||||
примере передается число 1). После возврата из вызываемой функции, будут выпол-
|
||||
няться операции выталкивания из стека (в примере команда "pop" будет выполнена
|
||||
1 раз). Следует помнить, что параметры восстанавливаются из стека вызывающей
|
||||
программой, а не вызываемой.
|
||||
С другой стороны, функция puts принадлежит к числу функций с фиксированными
|
||||
параметрами, поэтому адрес символьной строки передается через регистровую пару
|
||||
HL.
|
||||
|
||||
extern printf_, puts_
|
||||
|
||||
example: ld hl,msg
|
||||
push hl ; поместить в стек адрес символьной строки
|
||||
ld hl,1 ; загрузить число параметров
|
||||
call printf_ ; вызов процедуры, формирующей вывод
|
||||
pop hl ; выталкивание параметров из стека
|
||||
;
|
||||
ld hl,msg ; загрузить адрес строки
|
||||
jp puts_ ; вызов функции вывода строки
|
||||
|
||||
msg: db 'Hello, world!',0Ah,0
|
||||
|
||||
|
||||
|
||||
РЕКОМЕНДАЦИИ ПО ПРОГРАММИРОВАНИЮ
|
||||
|
||||
1. Следует пользоваться нерекурсивными функциями, если нет особой необхо-
|
||||
димости использовать рекурсию. Благодаря такому подходу, можно достичь более
|
||||
быстрого доступа к тем локальным переменным, для которых не распределяется
|
||||
регистровая память и следовательно, получить эффективный объектный код.
|
||||
|
||||
2. Если указан режим совместимости со стандартным языком Си (опция "-k"),
|
||||
то качество объектного кода будет ниже, поэтому указание на этот режим нужно
|
||||
применять только в случаях, когда требуемая совместимость на уровне стандарт-
|
||||
ного языка Си нужна больше, чем эффективность кода.
|
||||
|
||||
3. Используйте тип "char" (с 8-битовой длиной данных) везде, где только
|
||||
можно. Байтовые операции выполняются с помощью 8-битового процессора более
|
||||
эффективно, чем над данными, длиной в слово (типы "int" или "unsigned").
|
||||
Поэтому определяйте переменные, числовые значения которых не выходят за пре-
|
||||
делы диапазона 0..255, как TINY (char).
|
||||
|
||||
4. При использовании 16-битовых переменных следует определять их преимущест-
|
||||
венно типом "unsigned", который в этих случаях предпочтительнее, нежели "int".
|
||||
Это обусловлено тем, что в процессоре Z80 выполнение операций со знаком (в част-
|
||||
ности, операций сравнения величин со знаком) реализовано не лучшим образом.
|
||||
|
||||
5. Нежелательно установление статического режима для локальных переменных.
|
||||
Переменные, определенные как статические или внешние, не подлежат загрузке в
|
||||
регистровую память. Поэтому, если для повышения скорости выполнения программы
|
||||
определить локальные переменные как "static", а функции как "extern", то
|
||||
результат будет противоположным - эффективность объектного кода существенно
|
||||
понизится. Компилятор SOLID C сам производит распределение регистров, поэтому
|
||||
следует обязательно определять локальные переменные, как "auto".
|
||||
|
||||
|
||||
|
||||
ЗАРЕЗЕРВИРОВАННЫЕ СЛОВА ЯЗЫКА
|
||||
|
||||
Следующие слова используются как ключевые в компиляторе SOLID C и их нельзя
|
||||
использовать в качестве меток или имен переменных.
|
||||
|
||||
asm float signed
|
||||
auto for static
|
||||
break goto struct
|
||||
case if switch
|
||||
char int typedef
|
||||
const long union
|
||||
continue nonrec unsigned
|
||||
default recursive void
|
||||
do register volatile
|
||||
double return while
|
||||
else short
|
||||
extern sizeof
|
||||
|
||||
|
||||
|
||||
ДИРЕКТИВЫ КОМПИЛЯТОРА
|
||||
|
||||
define nonrec
|
||||
endif optimize
|
||||
error pragma
|
||||
ifdef recursive
|
||||
ifndef size
|
||||
include time
|
||||
line undef
|
||||
Vendored
+2570
File diff suppressed because it is too large
Load Diff
Vendored
+121
@@ -0,0 +1,121 @@
|
||||
ПРИНЦИП РАЗДЕЛЬНОЙ КОМПИЛЯЦИИ
|
||||
|
||||
|
||||
При разработке больших программ исходный текст разделен на несколько файлов.
|
||||
После того, как они отдельно друг от друга откомпилированы, их нужно скомпоно-
|
||||
вать в единую программу. Здесь описан метод раздельной компиляции и отмечены
|
||||
основные моменты, на которые следует обращать внимание при разработке исходного
|
||||
текста.
|
||||
|
||||
|
||||
1. Замечания к исходной программе
|
||||
|
||||
Имеются две серьезные проблемы раздельной компиляции и сборки больших
|
||||
программ:
|
||||
|
||||
- ссылки на функции из других файлов
|
||||
- возможность получения общих данных
|
||||
|
||||
Ниже дана попытка освещения этих вопросов с параллельным показом на приме-
|
||||
рах. Рассмотрим следующую программу.
|
||||
|
||||
#include <stdio.h>
|
||||
int a[10];
|
||||
char b[4];
|
||||
|
||||
char main()
|
||||
{
|
||||
char sub1(),sub2();
|
||||
sub1();
|
||||
sub2();
|
||||
printf("%d,%d,%d\n",a[0],a[3],a[9]);
|
||||
printf("%c,%c,%c\n",b[0],b[1],b[2]);
|
||||
}
|
||||
|
||||
char sub1()
|
||||
{
|
||||
a[0]=1;
|
||||
a[3]=2;
|
||||
a[9]=3;
|
||||
}
|
||||
|
||||
char sub2()
|
||||
{
|
||||
b[0]='a';
|
||||
b[1]='b';
|
||||
b[2]='c';
|
||||
}
|
||||
|
||||
Текст данной программы размещен в трех файлах с именами PROG1, PROG2 и
|
||||
PROG3 - соответственно содержащих главную функцию и функции 1 и 2.
|
||||
Точнее, в каждом из этих файлов содержатся:
|
||||
|
||||
PROG1.C:
|
||||
#include <stdio.h>
|
||||
int a[10];
|
||||
char b[4];
|
||||
char main()
|
||||
{
|
||||
:
|
||||
:
|
||||
}
|
||||
|
||||
PROG2.C:
|
||||
extern int a[10];
|
||||
char sub1()
|
||||
{
|
||||
:
|
||||
:
|
||||
}
|
||||
|
||||
PROG3.C:
|
||||
extern char b[4];
|
||||
char sub2()
|
||||
{
|
||||
:
|
||||
:
|
||||
}
|
||||
|
||||
Функция main вызывает функции sub1 и sub2, поэтому их определения необхо-
|
||||
димы. Так как функции sub1 и sub2 в свою очередь никакие другие функции не
|
||||
вызывают - никаких других описаний не требуется. Использование массивов "a"
|
||||
и "b" также является проблемой. На идентификатор "а" ссылаются программа main
|
||||
и sub1. На массив "b" ссылаются функции main и sub2. В любом из этих вариантов
|
||||
между файлами должна быть установлена связь.
|
||||
Для этой цели используется описатель "extern" (внешний). В первом из файлов
|
||||
- PROG1.C, этот описатель отсутствует, однако в двух других файлах этот описа-
|
||||
тель введен. Этот факт отражает то обстоятельство, что массивы "a" и "b" содер-
|
||||
жатся в файле PROG1.C, а в других файлах этих массивов нет. Если в двух или
|
||||
большем числе файлов будут стоять определения без описателей "extern", то при
|
||||
сборке единой программы появятся сообщения о дублировании имен массивов. С
|
||||
другой стороны, при добавлении описателя "extern" во все определения, они мо-
|
||||
гут диагностироваться как неопределенные.
|
||||
Следовательно, в случае организации общих данных для нескольких файлов необ-
|
||||
ходимо дать определение без описателя "extern" только в одном из файлов с
|
||||
текстом исходной программы. Во все другие файлы нужно поставить "extern".
|
||||
|
||||
|
||||
2. Процедура компиляции и сборки программы
|
||||
|
||||
Для описанных выше текстовых файлов с исходной программой последователь-
|
||||
ность команд компиляции и сборки модулей выглядит следующим образом:
|
||||
|
||||
cc1 prog1
|
||||
cc1 prog2
|
||||
cc1 prog3
|
||||
cc2 prog1
|
||||
cc2 prog2
|
||||
cc2 prog3
|
||||
as prog1
|
||||
as prog2
|
||||
as prog3
|
||||
ld prog=prog1,prog2,prog3,clib/l/gXMAIN
|
||||
|
||||
В том случае, когда все части программы (все файлы) уже откомпилированы и
|
||||
требуется лишь перекомпилировать одни из них (например файл PROG2.C), следует
|
||||
выполнить приведенную ниже группу команд:
|
||||
|
||||
cc1 prog2
|
||||
cc2 prog2
|
||||
as prog2
|
||||
ld prog=prog1,prog2,prog3,clib/l/gXMAIN
|
||||
Vendored
+228
@@ -0,0 +1,228 @@
|
||||
СОЗДАНИЕ БИБЛИОТЕК
|
||||
|
||||
|
||||
Известны два способа создания библиотеки. Оба они будут описаны в следующих
|
||||
параграфах. Первый из них, метод "а" - быстрый и легкий по исполнению, однако
|
||||
к собираемым пользовательским программам прилинковываются в том числе и такие
|
||||
функции, которые не обязательно будут использоваться.
|
||||
В отличие от первого, способ "b" реализуется сложнее и требует большего вре-
|
||||
мени, но получаемые в соответствии с ним пользовательские программы будут со-
|
||||
держать в себе только те функции, которые заведомо применяются в программе.
|
||||
Стандартная библиотека компилятора "clib.irl" создается именно по способу "b".
|
||||
|
||||
|
||||
1. Простой способ создания библиотеки
|
||||
|
||||
Рассмотрим пример двух функций "power" и "log2":
|
||||
|
||||
int power(x,e)
|
||||
int x,e;
|
||||
{
|
||||
int y;
|
||||
for(y=1; e>0; e--)
|
||||
y *= x;
|
||||
return(y);
|
||||
}
|
||||
|
||||
int log2(x)
|
||||
int x;
|
||||
{
|
||||
int y;
|
||||
for(y=0; x>1; x>>=1)
|
||||
y++;
|
||||
return(y);
|
||||
}
|
||||
|
||||
Если эти функции помещаются в файл с именем XLIB.C, последовательность
|
||||
создания библиотеки выглядит следующим образом:
|
||||
|
||||
cc1 xlib
|
||||
cc2 xlib
|
||||
as xlib
|
||||
|
||||
При выполнении этой последовательности будет создана библиотека с именем
|
||||
XLIB.REL. Ниже приведена команда сборки программы, которая вызывает функции
|
||||
из этой библиотеки:
|
||||
|
||||
ld prog,xlib,clib/l/gXMAIN
|
||||
|
||||
В этом случае нет необходимости писать ключ /l после имени библиотеки XLIB,
|
||||
т.к. даже если его написать, тем не менее все функции, входящие в библиотеку
|
||||
XLIB, будут скомпонованы с программой "prog".
|
||||
|
||||
Здесь будет описан способ включения в библиотеку программ, написанных на
|
||||
Ассемблере. В качестве примеров взяты функции hex, ror и rol, помещенные в
|
||||
файл ALIB.ASM:
|
||||
|
||||
public hex_, ror_, rol_
|
||||
cseg
|
||||
hex_: ; convert 0..F -> '0'..'F'
|
||||
and 0Fh
|
||||
add a,90h
|
||||
daa
|
||||
adc a,40h
|
||||
daa
|
||||
ret
|
||||
|
||||
ror_: ; ror(c,n) n bit rotate right char
|
||||
inc e
|
||||
rorl: dec e
|
||||
ret z
|
||||
rrca
|
||||
jp rorl
|
||||
|
||||
rol_: ; rol(c,n) n bit rotate left char
|
||||
inc e
|
||||
roll: dec e
|
||||
ret z
|
||||
rlca
|
||||
jp roll
|
||||
|
||||
Включение этих функций в библиотеку производится путем выполнения следующей
|
||||
команды:
|
||||
|
||||
as alib.asm
|
||||
|
||||
Следующая команда вызывает линковку программы, как и в случае Си-программы:
|
||||
|
||||
ld prog,alib,clib/l/gXMAIN
|
||||
|
||||
Как видно из приведенных выше примеров, самый простой способ формирования
|
||||
библиотеки - включение в нее тех программ, из которых компонуется программа.
|
||||
Однако этот способ не совершенен, так как вызывает подключение всей библиотеки.
|
||||
В следующем параграфе будет описан способ создания библиотеки, при котором
|
||||
программа компонуется только из тех функций, к которым предполагаются обраще-
|
||||
ния.
|
||||
|
||||
|
||||
2. Создание полной библиотеки
|
||||
|
||||
2.1. Включение Си-программ в библиотеку
|
||||
|
||||
Здесь будет приведен тот же пример, что и выше - включение файла XLIB.C,
|
||||
содержащего тексты функций POWER и LOG2 в библиотеку.
|
||||
Вначале нужно "разделить" файл XLIB.C на составляющие его функции, записав
|
||||
их в отдельные файлы POWER.C и LOG2.C. Далее необходимо выполнить следующие
|
||||
команды:
|
||||
|
||||
cc1 power
|
||||
cc1 log2
|
||||
cc2 power
|
||||
cc2 log2
|
||||
as power
|
||||
as log2
|
||||
ol a xlib.irl power log2
|
||||
|
||||
При выполнении этой последовательности команд, происходит следующее:
|
||||
|
||||
1. Си-тексты функций транслируются в ассемблерные листинги.
|
||||
2. В результате ассемблирования получаем модули с расширением ".rel".
|
||||
3. С помощью библиотекаря OL из двух модулей с расширением ".rel"
|
||||
создается библиотека XLIB.IRL.
|
||||
|
||||
После создания библиотеки XLIB.IRL, файлы POWER.REL и LOG2.REL уже не нужны
|
||||
и их можно удалить.
|
||||
Применение библиотеки XLIB.IRL определяется следующей командой:
|
||||
|
||||
ld prog,xlib/l,clib/l/gXMAIN
|
||||
|
||||
После имени библиотеки XLIB нужен ключ /l как указание линкеру подключить
|
||||
к программе только необходимые ей функции.
|
||||
|
||||
При сборке программ, состоящих из большого числа файлов (функций), имеет
|
||||
значение порядок расположения функций. Ниже приведены два примера: один из
|
||||
них правильный, а другой неправильный.
|
||||
|
||||
Правильный пример:
|
||||
|
||||
char islower(c)
|
||||
char c;
|
||||
{
|
||||
return('a'<=c && c<='z');
|
||||
}
|
||||
|
||||
char toupper(c)
|
||||
char c;
|
||||
{
|
||||
return(islower(c) ? c-'a'-'A' : c);
|
||||
}
|
||||
|
||||
В данном примере в библиотеку включается файл X.C, состоящий из двух
|
||||
функций. В этом случае функция "islower" расположена в файле X.REL перед
|
||||
функцией "toupper". Поэтому при редактировании связей по ключу /l линкер
|
||||
не выдаст диагностику об ошибке типа отсутствия определения.
|
||||
|
||||
Но если написать вначале текст функции "toupper", возникает следующий
|
||||
неправильный пример:
|
||||
|
||||
char toupper(c)
|
||||
char c;
|
||||
{
|
||||
char islower();
|
||||
return(islower(c) ? c-'a'-'A' : c);
|
||||
}
|
||||
|
||||
char islower(c)
|
||||
char c;
|
||||
{
|
||||
return('a'<=c && c<='z');
|
||||
}
|
||||
|
||||
В результате расположение модулей в библиотечном файле будет неправильным.
|
||||
Подводя итоги сказанному, можно резюмировать, что для правильного порядка
|
||||
расположения модулей в библиотеке нужно в исходной программе вначале писать
|
||||
вызываемую функцию, а после нее - вызывающую.
|
||||
|
||||
|
||||
2.2. Создание библиотеки модулей на Ассемблере
|
||||
|
||||
По сравнению с описанием включения в библиотеку, которое было дано в п.1,
|
||||
здесь, файл ALIB.ASM следует предварительно разделить на следующие три файла
|
||||
"hex.asm", "ror.asm" и "rol.asm":
|
||||
|
||||
HEX.ASM:
|
||||
cseg
|
||||
|
||||
hex_:: and 0Fh
|
||||
add a,90h
|
||||
daa
|
||||
adc a,40h
|
||||
daa
|
||||
ret
|
||||
|
||||
|
||||
ROR.ASM:
|
||||
cseg
|
||||
|
||||
ror_:: inc e
|
||||
rorl: dec e
|
||||
ret z
|
||||
rrca
|
||||
jp rorl
|
||||
|
||||
|
||||
ROL.ASM:
|
||||
cseg
|
||||
|
||||
rol_:: inc e
|
||||
roll: dec e
|
||||
ret z
|
||||
rlca
|
||||
jp roll
|
||||
|
||||
После того, как каждый из них будет ассемблирован и получен файл с расши-
|
||||
рением ".rel", вызывается библиотекарь OL для создания библиотеки:
|
||||
|
||||
as hex
|
||||
as ror
|
||||
as rol
|
||||
ol a alib.irl hex ror rol
|
||||
|
||||
Применение библиотеки ALIB.IRL определяется следующей командой:
|
||||
|
||||
ld prog,alib/l,clib/l/gXMAIN
|
||||
|
||||
При сборке программы, точно таким же образом, как и для сборки Си-программы
|
||||
из модулей, должна соблюдаться последовательность записи исходного текста, а
|
||||
именно: вызываемая функция должна быть записана прежде, а вызывающая ее, вслед
|
||||
за ней.
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
Последняя редакция: 7.05.2004
|
||||
|
||||
|
||||
IRL формат
|
||||
~~~~~~~~~~
|
||||
|
||||
IRL-файл представляет собой индексированный REL-файл. Благодаря индекси-
|
||||
рованию улучшается работа линкера. В начале IRL-файла находятся 3 байта:
|
||||
|
||||
db старший ;+0 (File offset >> 14) & 0x7F
|
||||
db средний ;+1 (File offset >> 7) & 0x7F
|
||||
db младший ;+2 (File offset 1) & 0x7F
|
||||
|
||||
IRL-файл состоит из 3 секций: IRL-заголовок, Символьная таблица и REL-имидж.
|
||||
|
||||
|
||||
IRL-заголовок
|
||||
~~~~~~~~~~~~~
|
||||
Массив 128 байт в начале REL-файла. Используются только первые три байта,
|
||||
содержащие смещение до REL-имиджа. Остальные байты установлены в ноль:
|
||||
|
||||
db старш,средн,0 ; Смещение до REL-имиджа. Младший байт
|
||||
; всегда равен 0. Начало REL-имиджа вы-
|
||||
; равнивается на границу в 128 байтов.
|
||||
ds 125 ; Не используются и установлены в 0.
|
||||
|
||||
Чтобы вычислить смещение до REL-имиджа, надо "старший" и "средний" байты
|
||||
умножить на 128. Например для файла clib.irl: 0011h * 128 = 0880h
|
||||
|
||||
ld h,xx ;+0
|
||||
ld a,xx ;+1
|
||||
;a,h,l * 128
|
||||
add a,a
|
||||
ld l,a
|
||||
xor a
|
||||
srl h
|
||||
rr l
|
||||
srl h
|
||||
rr l
|
||||
rra
|
||||
;hl=ст.разряд
|
||||
;a=мл.разряд
|
||||
|
||||
Обратите внимание, что 7-й бит первого байта всегда 0. В не индексированном
|
||||
rel-файле, 7-й бит первого байта всегда 1 (первый бит записи "имя модуля").
|
||||
|
||||
|
||||
Символьная таблица
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Таблица начинается со смещения +80h от начала файла и состоит из множества
|
||||
символьных записей разной длины. Каждая запись имеет формат:
|
||||
|
||||
db ст,средн,мл ; Смещение от начала rel-имиджа до rel-модуля,
|
||||
; содержащего символьную строку (имя).
|
||||
db "SYMBOL" ; Символьная строка из 1..8 символов.
|
||||
db 0FEh ; Конец строки.
|
||||
|
||||
|
||||
Конец таблицы отмечается 0 в начале символьной строки:
|
||||
|
||||
db ст,средн,0 ; Размер REL-имиджа. Это не смещение на конец
|
||||
; файла в REL-имидже, а показывает число байт
|
||||
; REL-имиджа, включая выравнивание до границы
|
||||
; в 128 байт.
|
||||
db 0FEh ; Конец строки.
|
||||
|
||||
Примечание!: У irl-файлов от библиотекаря "OL", данная запись имеет формат:
|
||||
|
||||
db 0,0,0
|
||||
db 0FFh
|
||||
|
||||
После этой записи, таблица выравнивается (LIB использует байт 1Ah) до следу-
|
||||
ющей границы в 128 байт.
|
||||
|
||||
|
||||
REL-имидж
|
||||
~~~~~~~~~
|
||||
REL-имидж всегда начинается с границы в 128 байт. Состоит из обычных rel-
|
||||
данных. Чтобы преобразовать IRL-библиотеку в REL-библиотеку, надо удалить
|
||||
заголовок и символьную таблицу.
|
||||
Vendored
+138
@@ -0,0 +1,138 @@
|
||||
Линкер
|
||||
|
||||
|
||||
Линкер LD предназначен для сборки готовых программ или dll-библиотек
|
||||
из отдельных объектных модулей. В качестве объектных модулей могут высту-
|
||||
пать файлы rel-формата или библиотеки irl-формата (создаваемые библиотека-
|
||||
рем ol.exe). Описание "rel", "irl" форматов смотрите в файлах "rel.rus" и
|
||||
"irl.rus".
|
||||
|
||||
Строка вызова линкера имеет следующий формат:
|
||||
|
||||
LD [/keys] [outfile=] infile[/keys] [,infile[/keys] ...] [@ list]
|
||||
|
||||
где:
|
||||
keys - опции линкера
|
||||
outfile - имя выходного файла программы
|
||||
infile - файл "rel" или "irl" формата
|
||||
@ list - файл подстановок
|
||||
|
||||
|
||||
Пример вызова линкера:
|
||||
|
||||
LD hello.rel
|
||||
|
||||
После выполнения данной команды получится файл "hello.exe".
|
||||
|
||||
При своей работе, линкер может создавать на диске временные файлы. Поэтому
|
||||
для ускорения работы линкера рекомендуется использовать его на электронном
|
||||
диске.
|
||||
|
||||
Опции линкера:
|
||||
|
||||
A использовать диск для сохранения симв. таблицы
|
||||
D## адрес расположения DATA сегмента
|
||||
E не вставлять EXE заголовок
|
||||
Glabel задать метку 'label' в качестве стартовой
|
||||
L поиск библиотечного файла (опция после имени файла)
|
||||
P## адрес расположения CODE сегмента
|
||||
Q вывести метки вида ?labels на экран
|
||||
R## установить адрес загрузки (адрес ORG-а программы)
|
||||
S вывести список меток на экран
|
||||
T[type:ver] создать DLL библиотеку. Type - внутр. описание библы;
|
||||
Ver - версия библы. Символ ":" разделяет параметры.
|
||||
U заменить все "@" на "_" в именах меток
|
||||
Y исключить DATA сегмент из выходного кода
|
||||
X не создавать SYM файл
|
||||
|
||||
|
||||
|
||||
Создание DLL-библиотек
|
||||
|
||||
Для создания динамических библиотек служит опция "t". Обратите внимание,
|
||||
что эта опция отменяет вставку exe-заголовка.
|
||||
Отслеживается превышение максимального размера dll-библиотеки в 16 kB.
|
||||
В этом случае линкер выдает предупреждающее сообщение, но линковка библи-
|
||||
отеки не прерывается.
|
||||
|
||||
Создаваемые dll-библиотеки имеют сигнатуру заголовка "L1". Библиотека этого
|
||||
формата отличается от формата "L0" только тем, что таблица перемещений начи-
|
||||
нается сразу за концом "своего" кода.
|
||||
Заголовок dll-библиотеки создается и прилинковывается автоматически, поэто-
|
||||
му не надо резервировать для него место при написании ассемблерных текстов.
|
||||
Более подробную информацию по dll-библиотекам смотрите в документации менед-
|
||||
жера dll-библиотек.
|
||||
|
||||
Пример строки вызова линкера:
|
||||
|
||||
LD /TSymple library:0001 test.rel
|
||||
|
||||
где:
|
||||
"Symple library" - Строка внутреннего описания библиотеки.
|
||||
Строка описания может содержать пробелы.
|
||||
Длина описания не должна превышать 16
|
||||
символов, иначе линкер выдаст ошибку.
|
||||
|
||||
"0001" - Внутренний номер версии библиотеки. Может
|
||||
содержать символы 0..9 и A..F (a..f).
|
||||
|
||||
"test.rel" - объектный файл, служащий для создания библиотеки.
|
||||
|
||||
Можно не указывать один из параметров (или все) опции, но символ ":" разде-
|
||||
лителя параметров должен стоять всегда. В случае отсутствия описания - в
|
||||
заголовке библиотеки будет стоять пустая строка. В случае отсутствия номера
|
||||
версии - в заголовке будет стоять нулевой номер. Примеры вызова опции:
|
||||
|
||||
LD /Tsymple library: test.rel
|
||||
LD /tМоя библиотека: test
|
||||
LD /T:010B test.rel
|
||||
LD /t:7 test
|
||||
LD /t: test.rel
|
||||
|
||||
Обратите внимание, что строка внутреннего описания библиотеки начинается
|
||||
сразу же за буквой "t" опции. Например, если написать
|
||||
|
||||
LD /T Example:0001 test.rel
|
||||
|
||||
то строка описания в заголовке библиотеки будет иметь вид " Example", т.е.
|
||||
начинаться с пробела.
|
||||
|
||||
|
||||
|
||||
Файл подстановок
|
||||
|
||||
Файл подстановок служит для некоторой "автоматизации" процесса линковки
|
||||
программ. Можно один раз создать файл подстановки и линковать программу, не
|
||||
набирая каждый раз (возможно длинную) строку параметров линкера.
|
||||
Файл подстановок может содержать всего одну строку агрументов или отдельные
|
||||
аргументы могут начинаться с новой строки. В конце файла подстановок должна
|
||||
стоять пустая строка. Размер файла подстановок ограничен до 256 байт и файл
|
||||
может иметь любое имя.
|
||||
Пример вызова линкера с файлами подстановок:
|
||||
|
||||
LD @ comp.txt
|
||||
|
||||
или
|
||||
|
||||
LD @list
|
||||
|
||||
|
||||
Примеры файла подстановок.
|
||||
|
||||
Пример 1
|
||||
|
||||
test=prog,clib.irl/l/gxmain/x
|
||||
|
||||
|
||||
Пример 2
|
||||
|
||||
test=prog,
|
||||
clib.irl/l
|
||||
/gxmain
|
||||
/x
|
||||
|
||||
В данных примерах собирается программа с заданным именем "test". При лин-
|
||||
ковке используется исходный объектный файл "prog.rel", библиотека "clib.irl"
|
||||
и управление в программе передается на метку "xmain" (это необходимо делать
|
||||
для всех программ, линкуемых с Си-библиотекой "clib.irl"). Опция "x" запре-
|
||||
щает создание sym-файла.
|
||||
Vendored
+1217
File diff suppressed because it is too large
Load Diff
Vendored
+1731
File diff suppressed because it is too large
Load Diff
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
Библиотекарь
|
||||
|
||||
|
||||
Библиотекарь OL служит для работы с библиотеками IRL-формата. Информацию
|
||||
по этому формату можно прочитать в файле "irl.rus". Строка вызова библио-
|
||||
текаря имеет следующий формат:
|
||||
|
||||
OL <опция> <имя_библиотеки> [<имя_модуля1> <имя_модуля2> ...]
|
||||
|
||||
Например:
|
||||
|
||||
OL a mylib.irl module1 module2 module3
|
||||
|
||||
В данном примере в библиотеку MYLIB.IRL добавляются три новых модуля
|
||||
(rel-файлы). Если файла MYLIB.IRL не существует, то создается новая
|
||||
библиотека с таким именем, содержащая три указанных модуля.
|
||||
|
||||
|
||||
Опции библиотекаря:
|
||||
|
||||
A - Добавить модуль(и) в библиотеку. Если указанной библиотеки
|
||||
нет, она создается.
|
||||
D - Вывод дампа содержимого библиотеки на экран.
|
||||
E - Извлечь модуль(и) из библиотеки и сохранить его в файле.
|
||||
В именах модулей можно использовать глобальные символы
|
||||
"*" и "?".
|
||||
L - Вывод списка модулей библиотеки на экран.
|
||||
R - Переиндексация библиотеки.
|
||||
T - Проверка всех модулей библиотеки на неопределенные внешние
|
||||
метки.
|
||||
Vendored
+155
@@ -0,0 +1,155 @@
|
||||
Некоторые особенности пакета SOLID C для ОС Estex
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
СИ-КОМПИЛЯТОР
|
||||
|
||||
Коды возврата компилятора (1-й и 2-й проходы):
|
||||
|
||||
00h - Ok
|
||||
0FFh - были errors/warnings, ошибки выделения памяти или работы с файлами.
|
||||
|
||||
|
||||
1-й проход компилятора
|
||||
|
||||
1. При компиляции "чужих" исходников ставить опцию "-k".
|
||||
|
||||
2. В опциях: -dNAME, -jN, -oNAME и -rP:S:H пробелы не допускаются.
|
||||
|
||||
3. Максимальная длина строки полного пути (disk\dir\file.ext) файлов
|
||||
равна 80 символов.
|
||||
|
||||
4. Файл "file.err" создается всегда по текущему пути входного .c файла.
|
||||
|
||||
5. Формат записи include-файлов только как:
|
||||
|
||||
#include <file.ext> - поиск ведется только в подкаталоге "INCLUDE"
|
||||
расположения компилятора.
|
||||
|
||||
#include "file.ext" - поиск ведется только в каталоге расположения
|
||||
входного .c файла.
|
||||
|
||||
Задание пути в имени файла не допускается!. Имя папки "INCLUDE" зарезерви-
|
||||
ровано.
|
||||
|
||||
2-й проход компилятора
|
||||
|
||||
1. Позволяет компилировать несколько бОльшие исходники, по сравнению
|
||||
с фирменной версией.
|
||||
|
||||
2. Исправлены две фирменные ошибки:
|
||||
a) нельзя было указывать расширение для TMC-файлов
|
||||
b) зависание при компиляции запредельно больших исходников
|
||||
|
||||
|
||||
|
||||
АССЕМБЛЕР
|
||||
|
||||
Примечание: Поскольку ассемблер AS совместим с ассемблером M80 фирмы Microsoft
|
||||
по формату записи ассемблерных текстов - в качестве документации на AS исполь-
|
||||
зуется документация ассемблера M80. Отличие ассемблера AS от M80 заключается в
|
||||
том, что у AS отсутствуют некоторые команды M80 (в основном, управление вывода
|
||||
листингов).
|
||||
|
||||
1. Максимальная длина имен (включающих букву диска и каталоги) файлов
|
||||
равна 64 символа.
|
||||
|
||||
2. В псевдокоманде NAME('...') можно использовать также двойные (")
|
||||
кавычки.
|
||||
|
||||
3. Имя include-файла можно указывать без расширения. В этом случае будет
|
||||
использоваться расширение по-умолчанию ".asm". Это же правило действует
|
||||
и для входного asm-файла.
|
||||
|
||||
4. Поддерживаются три варианта записи недокументированных регистров:
|
||||
|
||||
HX/XH LX/XL
|
||||
HY/YH LY/YL
|
||||
HIX/IXH LIX/IXL
|
||||
|
||||
5. Псевдокоманды "EQU" и "=" идентичны.
|
||||
|
||||
6. Максимальная длина имен глобальных меток, передаваемых в объектный
|
||||
файл (rel-файл) зависит от режима работы ассемблера:
|
||||
|
||||
6 символов - при указании опции "-t"
|
||||
8 символов - по-умолчанию
|
||||
30 символов - при указании опции "-x" (расширенный rel-формат)
|
||||
|
||||
Следует отметить, что с объектными файлами расширенного rel-формата не
|
||||
работает библиотекарь. Поэтому при компиляции библиотечных файлов, не
|
||||
следует ассемблеру указывать опцию "-x".
|
||||
|
||||
7. Коды возврата ассемблера:
|
||||
|
||||
00h - Ok
|
||||
0FFh - были errors/warnings, ошибки выделения памяти
|
||||
или работы с файлами.
|
||||
|
||||
|
||||
|
||||
ЛИНКЕР
|
||||
|
||||
При линковке готовых си-программ, собираемых с библиотекой "clib.irl" необхо-
|
||||
димо всегда указывать линкеру опцию "/gXMAIN". Она передает управление на
|
||||
начальный startup-код си-программ. См. исходники библиотеки "clib".
|
||||
|
||||
1. Линкер работает только с именами входных файлов. Указание диска и пути
|
||||
расположения файлов не поддерживается.
|
||||
|
||||
2. Исправлены фирменные глюки:
|
||||
a) неверная работа с "@list" файлом
|
||||
b) не отслеживались запросы на request-файлы
|
||||
|
||||
3. При вставке exe-заголовка, параметры равны: ORG = 4100h, Stack = 0C000h.
|
||||
(Значение ORG-a может быть изменено опцией "/r").
|
||||
|
||||
4. Файл "list" - содержит одну ком-строку с агрументами. Отдельные аргументы
|
||||
могут начинаться с новой строки. В конце файла должна стоять пустая новая
|
||||
строка. Кол-во читаемых байт ограничено до 256 байт.
|
||||
|
||||
5. Поиск библы: сперва файл *.irl, после *.rel. Если не найден, выдается
|
||||
сообщение ошибки: "файл *.rel не найден".
|
||||
|
||||
6. Максимальная длина имен идентификаторов - 30 символов.
|
||||
|
||||
7. Метки вида ?labels не выводятся в sym-файл и на экран. Становятся
|
||||
"видимыми" только при опции /Q.
|
||||
|
||||
8. Отслеживается превышение размера 16kB для dll-библиотек (выдается
|
||||
предупреждение).
|
||||
|
||||
9. Коды возврата линкера:
|
||||
|
||||
00h - Ok
|
||||
0FFh - были ошибки выделения памяти или работы с файлами.
|
||||
|
||||
|
||||
|
||||
БИБЛИОТЕКАРЬ
|
||||
|
||||
1. Тип открываемого входного файла отслеживается не по его расширению,
|
||||
а по внутреннему содержимому.
|
||||
|
||||
2. При создании библиотеки: если имя библиотеки совпадает с именем входного
|
||||
файла, то необходимо указать расширение библиотеки, чтобы не было двойного
|
||||
добавления файлов. В именах rel-файлов расширение можно не указывать. Имена
|
||||
добавляемых файлов разделяются пробелами или запятыми.
|
||||
|
||||
3. Глобальные символы "*" и "?" поддерживаются только для режима извлечения
|
||||
модулей (опция "e"). В остальных режимах необходимо указывать реальное имя
|
||||
файла.
|
||||
|
||||
4. Задание пути поддерживается только для файла библиотеки. Для rel-файлов
|
||||
указание пути не поддерживается и используется путь расположения библиотеки.
|
||||
|
||||
5. В опции l(ist) (список модулей библиотеки), размеры "Code size: ..." и
|
||||
"Data size: ..." выводятся в десятичном формате.
|
||||
|
||||
6. В опциях d(ump) и l(ist) вывод на экран можно остановить/продолжить при
|
||||
нажатии на любую клавишу. При нажатии на Esc - выход из режима.
|
||||
|
||||
7. Коды возврата библиотекаря:
|
||||
|
||||
00h - Ok
|
||||
0FFh - были ошибки работы или операций с файлами.
|
||||
Vendored
+137
@@ -0,0 +1,137 @@
|
||||
Последняя редакция: 7.05.2004
|
||||
|
||||
|
||||
ФОРМАТ REL-СОВМЕСТИМЫХ ОБЪЕКТНЫХ ФАЙЛОВ
|
||||
|
||||
|
||||
Объектные файлы REL-формата фирмы Microsoft, представляют собой битовый
|
||||
поток. Отдельные поля в битовом потоке не выровнены на границу байта,
|
||||
кроме элементов, описанных ниже.
|
||||
Использование битового потока для объектных файлов уменьшает их размер,
|
||||
сводя к минимуму число обращений к диску для чтения/записи данных.
|
||||
|
||||
Имеются два основных типа: Абсолютный и Перемещаемый. Первый бит служит
|
||||
индикатором типов. Если он равен 0, следующие 8 бит загружаются как абсо-
|
||||
лютный байт. Если первый бит равен 1, следующие 2 бита определяют 4 типа:
|
||||
|
||||
00b Спец-элемент (описание ниже).
|
||||
|
||||
01b Перемещаемый Код. Значение следующих 16 бит (2 байта)
|
||||
необходимо прибавить с текущему значению счетчика адреса
|
||||
Кода.
|
||||
|
||||
10b Перемещаемые Данные. Значение следующих 16 бит (2 байта)
|
||||
необходимо прибавить с текущему значению счетчика адреса
|
||||
Данных.
|
||||
|
||||
11b Перемещаемая Общая область (код+данные). Значение следующих
|
||||
16 бит (2 байта) необходимо прибавить с текущему значению
|
||||
счетчика адреса Common-области.
|
||||
|
||||
|
||||
Спец-элемент(ы) содержит:
|
||||
|
||||
■ контр. поле из 4-х бит номеров элементов 0..15.
|
||||
|
||||
■ не обязательное A-поле. Содержит 2 бита типа адресации cseg,
|
||||
dseg,common (кроме абсолютного) и 2 (4 у extended REL) байта
|
||||
значения.
|
||||
|
||||
■ не обязательное B-поле. Содержит 3 бита (5 у extended REL)
|
||||
длины имени и самого имени идентификатора.
|
||||
|
||||
|
||||
Общее представление спец-элемента:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1 00b xxx yy nnnn mmmm zzz + имя идентификатора
|
||||
-------------- ------------------------
|
||||
A-поле B-поле
|
||||
|
||||
xxx 4 бита контр. поля (0..15 номера элементов, см. ниже)
|
||||
|
||||
yy 2 бита типа адресации (cseg/dseg/common)
|
||||
nnnn 16 бит значения (адрес)
|
||||
mmmm 16 бит дополнительно, при расширенном REL-формате
|
||||
|
||||
zzz 3 бита длины имени (5 бит при расш. REL-формате)
|
||||
... имя идентификатора
|
||||
|
||||
|
||||
Номера элементов контрольного поля
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Номера элементов только в B-поле:
|
||||
|
||||
0 - имя метки, на которую имеется ссылка
|
||||
1 - имя блока COMMON
|
||||
2 - имя прогр. модуля (program name)
|
||||
3 - request-имя файла (request library search)
|
||||
4 - элемент расширения. См. примечание.
|
||||
|
||||
Номера элементов в A-поле и B-поле:
|
||||
|
||||
5 - размер COMMON (элемент только для A-поля ?)
|
||||
6 - внешняя цепочка:
|
||||
для A-поля: адрес головной цепочки
|
||||
для B-поля: внешнее имя
|
||||
7 - точка входа метки:
|
||||
для A-поля: адрес располож. метки
|
||||
для B-поля: имя метки
|
||||
|
||||
Номера элементов только в A-поле:
|
||||
|
||||
8 - External - offset. Used for JMP and CALL to externals.
|
||||
9 - External + offset. The A value will be added to the two
|
||||
bytes starting at the current location counter immediately
|
||||
before execution.
|
||||
10 - размер области Данных.
|
||||
11 - счетчик памяти сегмента (ORG cseg/dseg/common).
|
||||
12 - адрес цепочки. A is head of chain, replace all entries
|
||||
in chain with current location counter.
|
||||
Вход последней цепочки имеет нулевой адрес.
|
||||
13 - размер области Кода.
|
||||
14 - конец программного модуля (end program), далее идет
|
||||
выравнивание до границы байта.
|
||||
|
||||
Этот элемент не содержит ни A-поле, ни B-поле:
|
||||
|
||||
15 - конец файла
|
||||
|
||||
|
||||
Примечание по элементу расширения:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
C-поле элемента расширения имеет формат B-поля спец-элемента, но область
|
||||
имени не содержит имя идентификатора, как в B-поле. Вместо него здесь
|
||||
находится один байт типа (сигнатура) элемента расширения, далее следует
|
||||
от 1 до 7 байт дополнительной информации.
|
||||
|
||||
Таким образом, каждый элемент расширения имеет формат:
|
||||
|
||||
1 00b 0100b zzz i jjjjjjjj
|
||||
----------------
|
||||
C-поле
|
||||
|
||||
zzz любые 3 бита (000b представляется как 8).
|
||||
(001b у асма SOLID).
|
||||
|
||||
i 8 бит типа (сигнатура) элемента расширения.
|
||||
(0FEh у асма SOLID).
|
||||
|
||||
jjjjjjjj zzz-1 число байт информации. Значение зависит
|
||||
от типа i.
|
||||
|
||||
|
||||
Это присутствует только в одном элементе расширения:
|
||||
|
||||
zzz 010b (2)
|
||||
|
||||
i X'35' тип (сигнатура) оверлейного сегмента COBOL
|
||||
|
||||
j номер COBOL-сегмента - 31h (цифровой формат номера)
|
||||
|
||||
Когда линкеру встречается сигнатура оверлейного сегмента, текущее число
|
||||
оверлейных сегментов устанавливается в значение j+31h (симв. формат номера).
|
||||
Если предварительно существующий номер сегмента не был нулевой и включена
|
||||
опция /N (сохр. файл с заданным именем), то область данных записывается на
|
||||
диск в файл. Имя файла равно текущему имени программы, с расширением файла -
|
||||
Vxx, где "xx" две hex-цифры номера сегмента j+31h (симв. формат номера).
|
||||
Vendored
+129
@@ -0,0 +1,129 @@
|
||||
4.3 FORMAT OF LINK COMPATIBLE OBJECT FILES
|
||||
|
||||
|
||||
NOTE
|
||||
|
||||
Section 4.3 is interesting
|
||||
material for users who wish to
|
||||
know the load format of LINK-80
|
||||
relocatable object files. Most
|
||||
users will want to skip this
|
||||
section, as it does not contain
|
||||
material neccessary to the
|
||||
operation of the package.
|
||||
|
||||
|
||||
LINK-compatible object files consist of a bit stream. Individual fields
|
||||
within the bit stream are not aligned on byte boundaries, except as
|
||||
noted below. Use of a bit stream for relocatable object files keeps the
|
||||
size of object files to a minimum, thereby decreasing the number of
|
||||
disk reads/writes.
|
||||
|
||||
There are two basic types of load items: Absolute and Relocatable. The
|
||||
first bit of an item indicates one of these two types. If the first bit
|
||||
is a 0, the following 8 bits are loaded as an absolute byte. If the
|
||||
first bit is a 1, the next 2 bits are used to indicate one of four
|
||||
types of relocatable items:
|
||||
|
||||
00 Special LINK item (see below).
|
||||
|
||||
01 Program Relative. Load the following 16 bits
|
||||
after adding the current Program base.
|
||||
|
||||
10 Data Relative. Load the following 16 bits after
|
||||
adding the current Data base.
|
||||
|
||||
11 Common relative. Load the following 16 bits
|
||||
after adding the current Common base.
|
||||
|
||||
Special LINK items consist of the bit stream 100 followed by:
|
||||
|
||||
a four-bit control field
|
||||
|
||||
an optional A field consisting of a two-bit address type
|
||||
that is the same as the two-bit field above except 00
|
||||
specifies absolute address
|
||||
|
||||
an optional B field consisting of 3 bits that give a
|
||||
symbol length and up to 8 bits for each character of the
|
||||
symbol
|
||||
|
||||
A general representation of a special LINK item is:
|
||||
|
||||
1 00 xxxx yy nn zzz + characters of symbol name
|
||||
-------- ---------------------------------
|
||||
A field B field
|
||||
|
||||
xxxx Fout-bit control field (0-15 below)
|
||||
yy Two-bit address type field
|
||||
nn Sixteen-bit value
|
||||
zzz Three-bit symbol length field
|
||||
|
||||
The following special types have a B-field only:
|
||||
|
||||
0 Entry symbol (name for search)
|
||||
1 Select COMMON block
|
||||
2 Program name
|
||||
3 Request library search
|
||||
4 Extension LINK items (see below)
|
||||
|
||||
The following special LINK items have both an A field and a B field:
|
||||
|
||||
5 Define COMMON size
|
||||
6 Chain external (A is head of address chain, B is name of
|
||||
external symbol)
|
||||
7 Define entry point (A is address, B is name)
|
||||
|
||||
The following special LINK items have an A field only:
|
||||
|
||||
8 External - offset. Used for JMP and CALL to externals
|
||||
9 External + offset. The A value will be added to the two
|
||||
bytes starting at the current location counter
|
||||
immediately before execution.
|
||||
10 Define size of Data area (A is size)
|
||||
11 Set loading location counter to A
|
||||
12 Chain address. A is head of chain, replace all entries
|
||||
in chain with current location counter. The last entry
|
||||
in the chain has an address field of absolute zero
|
||||
13 Define program size (A is size)
|
||||
14 End program (forces to byte boundary)
|
||||
|
||||
The following special LINK item has neither an A nor a B field:
|
||||
|
||||
15 End file
|
||||
|
||||
An Extension LINK item follows the general format of a B-field-only
|
||||
special LINK item, but contents of the B-field are not a symbol name.
|
||||
Instead, the symbol area contains one character to identify the type of
|
||||
Extension LINK item, followed by from 1 to 7 characters of additional
|
||||
information.
|
||||
|
||||
Thus, every Extension LINK item has the format:
|
||||
|
||||
1 00 0100 zzz i jjjjjjjj
|
||||
|
||||
where
|
||||
|
||||
zzz may be any three bit integer (with 000 representing 8),
|
||||
|
||||
i is an eight bit Extension LINK item type indentifier,
|
||||
and
|
||||
|
||||
jjjjjjjj are zzz-1 eight bit character of information whose
|
||||
significance depends on i
|
||||
|
||||
At present, there is only one Extension LINK item:
|
||||
|
||||
i = X'35' COBOL overlay segment sentinel
|
||||
|
||||
zzz = 010 (binary)
|
||||
|
||||
j = COBOL segment number -49 (decimal)
|
||||
|
||||
When the overlay segment sentinal is encountered by the linker,
|
||||
the current overlay segment number is set to the value of j+49. If
|
||||
the previously existing segment number was non-zero and a /N
|
||||
switch is in effect, the data area is written to disk in a file
|
||||
whose name is the current program name and whose extension is Vnn,
|
||||
where nn are the two hexadecimal digits representing the number
|
||||
j+49 (decimal).
|
||||
Reference in New Issue
Block a user