http://www.aros.org AROS-Exec AROS-Exec Archives Power2People
kitty mascottop logo menu

Руководство по системному программированию AROS

Предупреждение

Этот документ не окончен! Вполне возможно, что многие части устарели, содержат некорректную информацию или полностью отсутствуют. Если вы хотите исправить их, пожалуйста сообщите нам.

Лицензионная политика

Почти весь программный код, написанный командой разработчиков AROS, предоставляется по лицензии AROS Public License (APL) и рекомендуется публиковать весь новый код, написанный для проекта, именно под ней.

Несмотря на это, мы понимаем, что это не всегда возможно. Например, мы зачастую хотим использовать хорошие программные библиотеки и приложения, написанные сторонними разработчиками вместо того, чтобы заново изобретать велосипед. Поэтому допускается внесение в SVN-репозиторий стороннего кода, предоставляемого не по лицензии APL в тех случаях, когда лицензия этого кода удовлетворяет требованиям, указанным ниже.

Требования к исходному коду в ветке contrib

При включении исходного кода в ветку contrib должны быть соблюдены следующие требования:

  1. Лицензия должна позволять нам:

    1. Распространять исходный код.
    2. Распространять исполняемые файлы.

    Если необходимы изменения исходного кода для его компилирования и работы в AROS, то лицензия должна дополнительно позволять вносить изменения в исходный код и распространять изменённый исходнй код и исполняемые файлы, содержащие эти изменения.

  2. Лицензия должна быть подробно изложена в файле по имени LEGAL, расположенном в корневой директории исходного кода, к которому она относится.

Требования к исходному коду в основной ветке AROS

Для включения в основную ветку AROS стороннего исходного кода, предоставляемого не по лицензии APL должны быть соблюдены следующие требования:

  1. Исходный код, требуемый для сборки какого-либо компонента или от которого зависит какой-либо компонент (как соответствующий, так и несоответствующий APL), который мы хотим включить в виде исполняемых файлов в базовый дистрибутив AROS.
  2. Лицензия должна быть открытой в соответствии с тем, как это определено Open Source Initiative (OSI), а значит позволять:
    1. Модифицировать исходный код.
    2. Повторно распространять исходный код (возможно, с модификациями).
    3. Повторно распространять исполняемые двоичные файлы (возможно созданные на основе модифицированного исходного кода).
  3. Лицензия не должна конфликтовать с APL:
    1. Если это одиночная программа, допускается почти любая лицензия, удовлетворяющая условиям пункта (2).
    2. Если это программная библиотека, то лицензия должна позволять компоновку с программами и библиотеками, использующими другие лицензии без каких-либо проблем. Это означает, что библиотеки под лицензией GPL (в отличие LGPL) не допускаются.
  4. Лицензия должна быть подробно изложена в файле 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 пробела. Два уровня отступа можно сократить до одного символа табуляции (таба).

Причины для этого следующие:

  1. Так как в некоторых редакторах используестя произвольный размер табов, это немного усложняет разъяснение другому редактору того, табы какого размера были использованы при написании кода.
  2. Большая часть программного кода в AROS оформлено именно так и ваш код будет плохо выглядеть в его контексте.
  3. Вы сможете распечатать этот код на любом принтере без использования специальных иснструментов для исправления табов.
  4. Большинство редакторов имеют настраиваемые табы, которые по умолчанию настроены именно так. Если ваш редактор не из таких, то напишите его создателям о найденной ошибке :-)
  • Если ваша функция имеет много аргументов (см. 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 на новый тип аппаратного обеспечения.

  1. Выберите идентифицирующее имя для вашего ЦПУ (например, i386, m68k, hppa, sparc) и добавьте к нему либо "-emul" (например, i386-emul), если ваша портированная версия будет работать в качестве гостевой ОС, либо "-native" (например, m68k-native), если портированная версия будет самостоятельной ОС.

  2. Выберите идентифицирующее имя для вашей системы (например, sgi, linux, amiga и т.д.).

  3. Отредактируйте скрипт "configure" и сделайте так, чтобы он распознавал ваше аппаратное обеспечение и настройте многочисленные переменные, которые требует ваша система.

    KERNEL

    Тип используемого вами ЦПУ (см. п. 1.).

    ARCH

    Имя вашей системы (см. п. 2.).

    SYS_CC

    Имя компилятора языка C в вашей системе.

    COMMON_CFLAGS

    флаги, которые необходимо вводить при каждом вызове компилятора C (например, -g -Wall -O0 и т.д.).

    ILDFLAGS

    Флаг, который вы должны задать компилятору при компоновке для предотвращения использования им любых стандартных библиотек или загрузочных модулей (для GCC это опции -nostartfiles -nostdlib -Xlinker -i). Это используется для создания исполняемых файлов в AROS. Эти исполняемые файлы не должны иметь никаких неразрешённых символов и все ссылки должны быть не пустыми.

    RANLIB

    Содержит имя вашей ranlib-программы. Если у вас такой нет, укажите здесь "true" (или имя любой другой команды системной оболочки, которая просто игнорирует все параметры и не возвращает код ошибки).

  4. Введите "make". Она аварийно звершится, т.к. здесь ещё нет $(KERNEL), но при этом будут установлены некоторые важные файлы и создастся дерево директорий.

  5. Скопируйте i386-emul в $(KERNEL) и перепишите весь исходный код на ассемблере для x86 в ассемблерный код для вашего ЦПУ.

  6. Наполните директорию $(KERNEL)/. Рекомендуется сделать копию из версии i386-emul, т.к. это наиболее свежая версия ядра.

  7. Введите "make machine". Это скомплирует программу и запустит её. Вывод этой команды может быть использован для внесения изменений в $(KERNEL)/machine.h.

  8. Запустите "make machine.i" в директории $(KERNEL). Это сгенерирует файл "machine.i", который понадобится вам для компилирования исходников на ассемблере. "machine.i" содержит значения множества системных констант (смещения вектров функций, смещения структур полей и системные флаги).

  9. Отредактируйте все файлы #?.s в директории $(KERNEL) и сгенерируйте соответствующий машинный код для вашего ЦПУ. Для компилирования файлов введите "make".

  10. Перейдите в главную директорию и введите "make". Если обнаружатся какие-либо ошибки, запишите их, исправьте и повторите этот пункт.

  11. Перейдите в директорию bin/$(ARCH)/AROS и запустите "arosshell". Теперь вы можете ввести некоторые команды, например, "dir all", "list" или "demowin". Если всё работает хорошо, то вы увидите список директорий и файлов по команде "dir all", а команда "demowin" откроет окно с несколькими устройствами и визуализацией, с которыми вы можете поиграться. Для выхода из демонстрационного окна введите "Esc" или щёлкните курсором мыши на надписи "Exit". Для останова "arosshell" вам надо нажать ^C (Ctrl-C), так как и в реальной ОС нет способа завершить её более правильно.


Copyright © 1995-2024, The AROS Development Team. Все права защищены.
Amiga© является торговым знаком Amiga Inc. Все прочие торговые знаки принадлежат их собственникам.