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
+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
|
||||
Reference in New Issue
Block a user