Руководство по системному программированию AROS
Предупреждение
Этот документ не окончен! Вполне возможно, что многие части устарели,
содержат некорректную информацию или полностью отсутствуют. Если
вы хотите исправить их, пожалуйста сообщите нам.
Почти весь программный код, написанный командой разработчиков AROS,
предоставляется по лицензии AROS Public License (APL) и рекомендуется
публиковать весь новый код, написанный для проекта, именно под ней.
Несмотря на это, мы понимаем, что это не всегда возможно. Например, мы зачастую
хотим использовать хорошие программные библиотеки и приложения, написанные
сторонними разработчиками вместо того, чтобы заново изобретать велосипед.
Поэтому допускается внесение в SVN-репозиторий стороннего кода, предоставляемого
не по лицензии APL в тех случаях, когда лицензия этого кода удовлетворяет
требованиям, указанным ниже.
При включении исходного кода в ветку contrib должны быть соблюдены следующие
требования:
Лицензия должна позволять нам:
- Распространять исходный код.
- Распространять исполняемые файлы.
Если необходимы изменения исходного кода для его компилирования и работы в
AROS, то лицензия должна дополнительно позволять вносить изменения в
исходный код и распространять изменённый исходнй код и исполняемые
файлы, содержащие эти изменения.
Лицензия должна быть подробно изложена в файле по имени LEGAL, расположенном
в корневой директории исходного кода, к которому она относится.
Для включения в основную ветку AROS стороннего исходного кода, предоставляемого
не по лицензии APL должны быть соблюдены следующие требования:
- Исходный код, требуемый для сборки какого-либо компонента или от которого
зависит какой-либо компонент (как соответствующий, так и несоответствующий
APL), который мы хотим включить в виде исполняемых файлов в базовый
дистрибутив AROS.
- Лицензия должна быть открытой в соответствии с тем, как это определено Open
Source Initiative (OSI), а значит позволять:
- Модифицировать исходный код.
- Повторно распространять исходный код (возможно, с модификациями).
- Повторно распространять исполняемые двоичные файлы (возможно созданные на
основе модифицированного исходного кода).
- Лицензия не должна конфликтовать с APL:
- Если это одиночная программа, допускается почти любая лицензия,
удовлетворяющая условиям пункта (2).
- Если это программная библиотека, то лицензия должна позволять компоновку с
программами и библиотеками, использующими другие лицензии без каких-либо
проблем. Это означает, что библиотеки под лицензией GPL (в отличие LGPL)
не допускаются.
- Лицензия должна быть подробно изложена в файле LEGAL, расположенном в корневой
директории исходного кода, к которому она относится.
Этот исходный код будет использоваться множеством людей, поэтому следует помнить
некоторые вещи при его опубликовании:
- Не усложняйте простое
- Не захламляйте исходники
- Постоянно отдавайте себе отчёт в том, что делаете
- Проговаривайте то, что делаете
- Помните, что однажды написанный вами код, будет прочитан много раз многими
людьми
Внимание! Комментарии следует писать на английском языке.
AROS использует некоторые комментарии к исходному коду для автоматического
генерирования документации. Поэтому необходимо придерживаться определённого
формата, чтобы документирующие программы могли найти нужную информацию. Прочие
комментарии ими игнорируются, но в них также следует раскрывать ход ваших мыслей
во время написания программы. Если вы действительно не можете объяснить, что
делает программа, то не надо описывать код буквально, как показано ниже:
/* Добавляем 1 к t */
t++;
В то время как на самом деле мы думаем так:
/* Переходим к следующему элементу */
t++;
Каждая функция в AROS должна иметь прототип, полностью соответствующий ANSI C.
Прототипы должны быть собраны вместе в одном заголовке на файл, если есть
необходимость только в нескольких файлах (не будет необходимости
перекомпилировать проект целиком, если вы изменили функцию вызываемую всего один
раз), в одном заголовке на директорию, если это часто используемая в этой
директории функция, или один заголовок на логическую группу (т.е. один заголовок
на все функции в программной библиотеке).
Заголовок функции (т.е. коментарий перед кодом функции) должен быть записан в
специальном формате, т.к. по нему автоматически генерируется документация.
Пример (из файла <filename>AROS/exec/addhead.c</filename>):
/*****************************************************************************
NAME */
#include <exec/lists.h>
#include <clib/exec_protos.h>
AROS_LH2I(void, AddHead,
/* SYNOPSIS */
AROS_LHA(struct List *, list, A0),
AROS_LHA(struct Node *, node, A1),
/* LOCATION */
struct ExecBase *, SysBase, 40, Exec)
/* FUNCTION
Insert Node node as the first node of the list.
INPUTS
list - The list to insert the node into
node - This node is to be inserted
RESULT
None.
NOTES
EXAMPLE
struct List * list;
struct Node * pred;
// Insert Node at top
AddHead (list, node);
BUGS
SEE ALSO
NewList(), AddTail(), Insert(), Remove(), RemHead(), RemTail(),
Enqueue()
INTERNALS
*****************************************************************************/
{
тело функции
}
Как вы видите, комментарии объединены воедино с прототипом функции и заголовком.
- NAME
Это поле содержит все необходимые с пользовательской точки зрения прототипы
для использования функции и имени функции в макросе AROS_LH#?() (Library
Header, Библиотечный заголовок). Эти макросы используются для обеспечения
работоспособности одного и того же программного кода на разных типах
аппаратного обеспечения. Имя макроса зависит от количества параметров и от
того, нужны ли для работы функции основные библиотеки. Функции AddHead() не
нужны и, следовательно, к имени макроса присоединяется буква "I". Если
библиотечная база нужна (например, для функции AddTask()), то буква "I"
не добавляется.
Если функция не является частью совместно используемой программной
библиотеки и её аргументы должны передаваться в определённый регистр
(например, callback hooks), то вы должны использовать макрос AROS_UFH#?
(User Function Header, Заголовок пользовательской функции) вместо
AROS_LH#?(). К имени макроса надо добавить несколько аргументов. Т.к.
этой функции никогда не было в основных библиотеках, то поле LOCATION надо
пропустить и нет необходимости в добавлении буквы "I" к имени макроса.
Например, callback hook foo() будет выглядеть так:
AROS_UFH3(ULONG, foo,
AROS_UFHA(struct Hook, hook, A0),
AROS_UFHA(APTR, obj, A2),
AROS_UFHA(APTR, param, A1)
)
(Заметим, что регистры не нуждаются в определенном порядке перечисления)
Если функция не является частью совместно используемой программной
библиотеки и её аргументы не должны находиться в определённых регистрах, то
макрос AROS_#?H#?() не нужен:
/*****************************************************************************
NAME */
#include <header.h>
int foo (
/* SYNOPSIS */
int a,
int b)
/* FUNCTION
blahblahblah.
...
*****************************************************************************/
- SYNOPSIS
Это поле содержит по порядку все аргументы функции в макросе AROS_LHA()
(Library Header Argument, Агрумент библиотечного заголовка). Этот макрос при
вызове функции обеспечивает расположение необходимого аргумента в правильном
регистре процессора (если возможно и необходимо). Первым аргументом для
макроса является тип параметра, за ним следуют имя параметра и регистр, в
котором параметр ожидается. Правильными именами регистров являются имена с
D0 до D7 и с A0 до A6.
Если функция не является частью программной библиотеки, но аргументы ей
должны передаваться через регистры, то используйте макрос AROS_UFHA()
(User Function Header Argument), требующий такие же параметры как и
макрос AROS_LHA(). Не забывайте закрывать скобки в AROS_UFC.
Если функция не является частью программной библиотеки и аргументы для неё
не должны передаваться через регистры, то в макросах нет необходимости.
- LOCATION
- Это поле необходимо только для совместно используемых программных библиотек.
Оно содержит последние значения четырёх параметров для макроса
AROS_LH#?(), которыми являются: тип библиотеки; имя переменной, в которой
функция ожидает основную библиотеку; смещение функции в таблице векторов
прерываний (первым вектором является 1, а первым вектором, который может
использовать функция является 5); имя библиотеки.
- FUNCTION
- Это поле содержит описание функции.
- INPUTS
- Это поле содержит список всех параметров функции в виде "имя - описание" или
"имя, имя, имя - описание". Описание должно говорить о том, что представляет
собой параметр и какое значение ему может быть передано. Не надо повторять
объяснение параметра дважды - в поле FUNCTION и здесь. Если функция не имеет
параметров, напишите в этом поле "None".
- RESULT
- То, что функция возвращает. Сюда относятся возвращаемые значения и значения,
передаваемые в качестве аргументов функции. Если функция может завершиться
неудачно, следует описать, что она возвратит в этом случае, и почему это
могло произойти.
- NOTES
- Важные вещи, которые пользователь функции должен знать или принимать во
внимание.
- EXAMPLE
Это поле должно содержать пример использования функции, либо небольшой, либо
демонстрирующий все её возможности. Хорошо для примера будет написать
фрагмент кода, который тестирует функцию, поместить его внутрь #ifdef TEST
где-нибудь в файле, а в этом поле написать "See below" (См. ниже). Для
написания комментариев, поясняющих программный код, есть 2 способа. Всё
написанное после // и до конца строки является комментарием. Если вам
нужно написать более длинный комментарий, то вы можете закончить комментарий
после EXAMPLE и использовать #ifdef EXAMPLE для сокрытия вывода
примера:
EXAMPLE */
#ifdef EXAMPLE
struct List * list;
struct Node * pred;
/* Insert Node at top of the list */
AddHead (list, node);
#endif
Не используйте #ifdef EXAMPLE, если вы пишете пример, демонстрирующий все
возможности функции (т.е. такой, который можно скопировать и скомпилировать
без ошибок).
- BUGS
- Это поле содержит список известных ошибок (багов).
- SEE ALSO
Это поле содержит список других функций и документов, которые могут быть
интересны в контексте данной функции. Сюда относятся: функции, которые вам
надо инициализировать; объект, который необходимо создать или уничтожить для
этой функции; функции, которые делают похожие и противоположные действия с
основным объектом.
Например, для функции SetAttrs() это поле должно содержать список функций,
которые могут создавать, уничтожать и манипулировать BOOPSI-объектами, но не
список тегов.
- INTERNALS
- Это поле должно содержать информацию для других разработчиков, которая не
имеет значения для пользователя функции, например, объяснение алгоритма
функции или зависимостей.
Здесь приведён пример форматирования кода AROS:
{
/* a */
struct RastPort * rp;
int a;
/* b */
rp = NULL;
a = 1;
/* c */
if (a == 1)
printf ("Init worked\n");
/* d */
if
(
!(rp = Get_a_pointer_to_the_RastPort
(
some
, long
, arguments
)
)
||
a <= 0
)
{
printf ("Something failed\n");
return FAIL;
}
/* e */
a = printf ("My RastPort is %p, a=%d\n"
, rp
, a
);
return OK;
}
Выглядит уродливо, да? :-) Хорошо, правила форматирования следующие:
- Если несколько строк содрежат похожий код, то размещайте сходные вещи друг под
другом (см. a и b).
- Оставляйте пробелы между операторами и операндами.
- Расставляйте фигурные {}, квадратные [] и круглые () скобки друг
под другом (см. d), если между ними содержится много кода. Квадратные и круглые
скобки могут быть в одной строке, если между ними небольшое количество кода
(см. c).
- Отступ в 4 пробела. Два уровня отступа можно сократить до одного символа
табуляции (таба).
Причины для этого следующие:
- Так как в некоторых редакторах используестя произвольный размер табов, это
немного усложняет разъяснение другому редактору того, табы какого размера
были использованы при написании кода.
- Большая часть программного кода в AROS оформлено именно так и ваш код будет
плохо выглядеть в его контексте.
- Вы сможете распечатать этот код на любом принтере без использования
специальных иснструментов для исправления табов.
- Большинство редакторов имеют настраиваемые табы, которые по умолчанию
настроены именно так. Если ваш редактор не из таких, то напишите его
создателям о найденной ошибке :-)
- Если ваша функция имеет много аргументов (см. d и e), то следует поместить
круглые скобки на отдельных строках и каждый аргумент на отдельной строке
(см. d) или поместить первый аргумент после открывающей круглой скобки
(см. e), а каждый последующий аргумент - на отдельной строке с запятой впереди.
Закрывающая скобка будет на отдельной строке и выровнена с началом выражения,
а не с открывающей скобкой (см. printf() в e).
- Используйте одиночную пустую линию для разделения логических блоков. Длинные
комментарии должны иметь пустую линию до и после себя. Короткие комментарии
следует размещать перед программным кодом, который они поясняют, с одиночной
пустой линией перед ними.
Код програмных модулей AROS должен быть написан в таком стиле, чтобы его можно
было записать в ПЗУ, флэш-ПЗУ или другие разновидности ПЗУ. Это можно сделать
посредством приведенных ниже правил стиля программирования. Разумеется, они
позволяют всем модулям Kickstart и коду, который можно сделать резидентным,
использоваться совместно или компоноваться с другими модулями.
ПЗУ-модули не должны иметь секций .data и .bss. В общем, надо избавиться от
всех непостоянных глобальных данных. Оригинальный Amiga Kickstart доказывает,
что это не только возможно, но и легко достижимо.
Если вы столкнулись с внутренней переменной (статической либо нет), которая
изменяется программой, попробуйте избавиться от неё или переместите её в
основу библиотеки/устройства (или в устройство (device node) вашего указателя
или в пользовательские данные вашего класса).
Сказанное относится и к основным библиотекам. Если вы пишете библиотеку,
поместите основы других библиотек в базовую структуру вашей собственной.
Классы BOOPSI могут хранить основные библиотеки в своих приватных
данных.
Попытайтесь установить аттрибуты static и const для всех ваших глобальных
данных. Вы также можете использовать типы CONST_STRPTR и CONST_APTR ,
определённые в <exec/types.h>. Использование static const позволяет
компилятору перемещать данные в сегмент ".text" (т.е. в код). Если вам надо
передать эти глобальные данные другой функции, попробуйте применить const
и для её прототипа. Помните, что с версии Amiga OS 3.5 Олаф Бартел
окончательно перешёл на использование const в заголовках <clib/#?_protos.h>.
НИКОГДА не трогайте буферы, передаваемые пользователем в качестве
"входного" параметра. Общее представление входных параметров зачастую не
указывается явно, "прячется" в описаниях функций. Например, имя файла, передаваемое
функции Open() определенно является входной переменной и Open() не должна
смешиваться с ней, даже если это позже будет исправлено. Учтите, что буфер
может располагаться в ПЗУ или использоваться совместно несколькими копиями
резидентной или многопоточной программы.
Пытайтесь избегать системных вызовов основной ОС, таких как malloc() и
free(), если вы можете сделать тоже самое при помощи AllocMem() и
FreeMem(). Это необходимо для того, чтобы отладочный макрос, проверяющий
указатели, нашёл указатель внутри блоков памяти Exec с помощью TypeOfMem().
Здесь описывается как портировать AROS на новый тип аппаратного обеспечения.
Выберите идентифицирующее имя для вашего ЦПУ (например, i386, m68k, hppa,
sparc) и добавьте к нему либо "-emul" (например, i386-emul), если ваша
портированная версия будет работать в качестве гостевой ОС, либо "-native"
(например, m68k-native), если портированная версия будет самостоятельной ОС.
Выберите идентифицирующее имя для вашей системы (например, sgi, linux, amiga
и т.д.).
Отредактируйте скрипт "configure" и сделайте так, чтобы он распознавал ваше
аппаратное обеспечение и настройте многочисленные переменные, которые требует
ваша система.
- KERNEL
Тип используемого вами ЦПУ (см. п. 1.).
- ARCH
Имя вашей системы (см. п. 2.).
- SYS_CC
Имя компилятора языка C в вашей системе.
- COMMON_CFLAGS
флаги, которые необходимо вводить при каждом вызове компилятора C (например,
-g -Wall -O0 и т.д.).
- ILDFLAGS
Флаг, который вы должны задать компилятору при компоновке для предотвращения
использования им любых стандартных библиотек или загрузочных модулей (для
GCC это опции -nostartfiles -nostdlib -Xlinker -i). Это используется для
создания исполняемых файлов в AROS. Эти исполняемые файлы не должны
иметь никаких неразрешённых символов и все ссылки должны быть не пустыми.
- RANLIB
Содержит имя вашей ranlib-программы. Если у вас такой нет, укажите здесь
"true" (или имя любой другой команды системной оболочки, которая просто
игнорирует все параметры и не возвращает код ошибки).
Введите "make". Она аварийно звершится, т.к. здесь ещё нет $(KERNEL), но при
этом будут установлены некоторые важные файлы и создастся дерево директорий.
Скопируйте i386-emul в $(KERNEL) и перепишите весь исходный код на
ассемблере для x86 в ассемблерный код для вашего ЦПУ.
Наполните директорию $(KERNEL)/. Рекомендуется сделать копию из версии
i386-emul, т.к. это наиболее свежая версия ядра.
Введите "make machine". Это скомплирует программу и запустит её. Вывод этой
команды может быть использован для внесения изменений в $(KERNEL)/machine.h.
Запустите "make machine.i" в директории $(KERNEL). Это сгенерирует файл
"machine.i", который понадобится вам для компилирования исходников на
ассемблере. "machine.i" содержит значения множества системных констант
(смещения вектров функций, смещения структур полей и системные флаги).
Отредактируйте все файлы #?.s в директории $(KERNEL) и сгенерируйте
соответствующий машинный код для вашего ЦПУ. Для компилирования файлов
введите "make".
Перейдите в главную директорию и введите "make". Если обнаружатся какие-либо
ошибки, запишите их, исправьте и повторите этот пункт.
Перейдите в директорию bin/$(ARCH)/AROS и запустите "arosshell". Теперь вы
можете ввести некоторые команды, например, "dir all", "list" или "demowin".
Если всё работает хорошо, то вы увидите список директорий и файлов по
команде "dir all", а команда "demowin" откроет окно с несколькими
устройствами и визуализацией, с которыми вы можете поиграться. Для выхода из
демонстрационного окна введите "Esc" или щёлкните курсором мыши на надписи
"Exit". Для останова "arosshell" вам надо нажать ^C (Ctrl-C), так как
и в реальной ОС нет способа завершить её более правильно.
|
|