Компилятор языка программирования Oberon-07/16 для микроконтроллеров STM32 Cortex-M3. ------------------------------------------------------------------------------ Параметры командной строки Вход - текстовые файлы модулей с расширением ".ob07", кодировка ANSI или UTF-8 с BOM-сигнатурой. Выход - hex-файл прошивки. Параметры: 1) имя главного модуля 2) "stm32cm3" 3) необязательные параметры-ключи -out имя результирующего файла; по умолчанию, совпадает с именем главного модуля, но с расширением ".hex" -ram размер ОЗУ в килобайтах (4 - 65536) по умолчанию 4 -rom размер ПЗУ в килобайтах (16 - 65536) по умолчанию 16 -tab размер табуляции (используется для вычисления координат в исходном коде), по умолчанию - 4 -nochk <"ptibcwra"> отключить проверки при выполнении -lower разрешить ключевые слова и встроенные идентификаторы в нижнем регистре (по умолчанию) -upper только верхний регистр для ключевых слов и встроенных идентификаторов -def <имя> задать символ условной компиляции -uses вывести список импортированных модулей параметр -nochk задается в виде строки из символов: "p" - указатели "t" - типы "i" - индексы "b" - неявное приведение INTEGER к BYTE "c" - диапазон аргумента функции CHR "w" - диапазон аргумента функции WCHR "r" - эквивалентно "bcw" "a" - все проверки Порядок символов может быть любым. Наличие в строке того или иного символа отключает соответствующую проверку. Например: -nochk it - отключить проверку индексов и охрану типа. -nochk a - отключить все отключаемые проверки. Например: Compiler.exe "C:\example.ob07" stm32cm3 -ram 32 -rom 256 -nochk pti Compiler.exe "C:\example.ob07" stm32cm3 -out "C:\Ex1.hex" -ram 8 -rom 32 В случае успешной компиляции, компилятор передает код завершения 0, иначе 1. ------------------------------------------------------------------------------ Отличия от оригинала 1. Расширен псевдомодуль SYSTEM 2. В идентификаторах допускается символ "_" 3. Усовершенствован оператор CASE (добавлены константные выражения в метках вариантов и необязательная ветка ELSE) 4. Расширен набор стандартных процедур 5. Семантика охраны/проверки типа уточнена для нулевого указателя 6. Добавлены однострочные комментарии (начинаются с пары символов "//") 7. Разрешено наследование от типа-указателя 8. "Строки" можно заключать также в одиночные кавычки: 'строка' 9. Добавлен тип WCHAR 10. Добавлена операция конкатенации строковых и символьных констант 11. Добавлены кодовые процедуры 12. Возможен импорт модулей с указанием пути и имени файла 13. Добавлен специальный синтаксис для условной компиляции (см. CC.txt) 14. Имя процедуры в конце объявления (после END) необязательно 15. Разрешено использовать нижний регистр для ключевых слов ------------------------------------------------------------------------------ Особенности реализации 1. Основные типы Тип Диапазон значений Размер, байт INTEGER -2147483648 .. 2147483647 4 REAL 1.17E-38 .. 3.40E+38 4 CHAR символ ASCII (0X .. 0FFX) 1 BOOLEAN FALSE, TRUE 1 SET множество из целых чисел {0 .. 31} 4 BYTE 0 .. 255 1 WCHAR символ юникода (0X .. 0FFFFX) 2 2. Максимальная длина идентификаторов - 255 символов 3. Максимальная длина строковых констант - 511 символов (UTF-8) 4. Максимальная размерность открытых массивов - 5 5. Процедура NEW заполняет нулями выделенный блок памяти 6. Локальные переменные инициализируются нулями 7. В отличие от многих Oberon-реализаций, сборщик мусора и динамическая модульность отсутствуют 8. Тип BYTE в выражениях всегда приводится к INTEGER 9. Контроль переполнения значений выражений не производится ------------------------------------------------------------------------------ Псевдомодуль SYSTEM Псевдомодуль SYSTEM содержит низкоуровневые и небезопасные процедуры, ошибки при использовании процедур псевдомодуля SYSTEM могут привести к повреждению данных времени выполнения и аварийному завершению программы. PROCEDURE ADR(v: любой тип): INTEGER v - переменная или процедура; возвращает адрес v PROCEDURE SADR(x: строковая константа (CHAR UTF-8)): INTEGER возвращает адрес x PROCEDURE WSADR(x: строковая константа (WCHAR)): INTEGER возвращает адрес x PROCEDURE VAL(v: любой тип; T): T v - переменная; интерпретирует v, как переменную типа T PROCEDURE SIZE(T): INTEGER возвращает размер типа T PROCEDURE TYPEID(T): INTEGER T - тип-запись или тип-указатель, возвращает номер типа в таблице типов-записей PROCEDURE INF(): REAL возвращает специальное вещественное значение "бесконечность" PROCEDURE MOVE(Source, Dest, n: INTEGER) Копирует n байт памяти из Source в Dest, области Source и Dest не могут перекрываться PROCEDURE GET(a: INTEGER; VAR v: любой основной тип, PROCEDURE, POINTER) v := Память[a] PROCEDURE GET8(a: INTEGER; VAR x: INTEGER, SET, BYTE, CHAR, WCHAR, SYSTEM.CARD32) Эквивалентно SYSTEM.MOVE(a, SYSTEM.ADR(x), 1) PROCEDURE GET16(a: INTEGER; VAR x: INTEGER, SET, WCHAR, SYSTEM.CARD32) Эквивалентно SYSTEM.MOVE(a, SYSTEM.ADR(x), 2) PROCEDURE GET32(a: INTEGER; VAR x: INTEGER, SET, SYSTEM.CARD32) Эквивалентно SYSTEM.MOVE(a, SYSTEM.ADR(x), 4) PROCEDURE PUT(a: INTEGER; x: любой основной тип, PROCEDURE, POINTER) Память[a] := x; Если x: BYTE или x: WCHAR, то значение x будет расширено до 32 бит, для записи байтов использовать SYSTEM.PUT8, для WCHAR -- SYSTEM.PUT16 PROCEDURE PUT8(a: INTEGER; x: INTEGER, SET, BYTE, CHAR, WCHAR, SYSTEM.CARD32) Память[a] := младшие 8 бит (x) PROCEDURE PUT16(a: INTEGER; x: INTEGER, SET, BYTE, CHAR, WCHAR, SYSTEM.CARD32) Память[a] := младшие 16 бит (x) PROCEDURE PUT32(a: INTEGER; x: INTEGER, SET, BYTE, CHAR, WCHAR, SYSTEM.CARD32) Память[a] := младшие 32 бит (x) PROCEDURE CODE(hword1, hword2,... : INTEGER) Вставка машинного кода, hword1, hword2 ... - константы в диапазоне 0..65535, например: SYSTEM.CODE(0BF30H) (* wfi *) Также, в модуле SYSTEM определен тип CARD32 (4 байта). Для типа CARD32 не допускаются никакие явные операции, за исключением присваивания. Функции псевдомодуля SYSTEM нельзя использовать в константных выражениях. ------------------------------------------------------------------------------ Оператор CASE Синтаксис оператора CASE: CaseStatement = CASE Expression OF Case {"|" Case} [ELSE StatementSequence] END. Case = [CaseLabelList ":" StatementSequence]. CaseLabelList = CaseLabels {"," CaseLabels}. CaseLabels = ConstExpression [".." ConstExpression]. Например: CASE x OF |-1: DoSomething1 | 1: DoSomething2 | 0: DoSomething3 ELSE DoSomething4 END В метках вариантов можно использовать константные выражения, ветка ELSE необязательна. Если значение x не соответствует ни одному варианту и ELSE отсутствует, то программа прерывается с ошибкой времени выполнения. ------------------------------------------------------------------------------ Тип WCHAR Тип WCHAR добавлен в язык для удобной поддежки юникода. Для типов WCHAR и ARRAY OF WCHAR допускаются все те же операции, как для типов CHAR и ARRAY OF CHAR, за исключением встроенной процедуры CHR, которая возвращает только тип CHAR. Для получения значения типа WCHAR, следует использовать процедуру WCHR вместо CHR. Для правильной работы с типом, необходимо сохранять исходный код в кодировке UTF-8 с BOM. ------------------------------------------------------------------------------ Конкатенация строковых и символьных констант Допускается конкатенация ("+") константных строк и символов типа CHAR: str = CHR(39) + "string" + CHR(39); (* str = "'string'" *) newline = 0DX + 0AX; ------------------------------------------------------------------------------ Проверка и охрана типа нулевого указателя Оригинальное сообщение о языке не определяет поведение программы при выполнении охраны p(T) и проверки типа p IS T при p = NIL. Во многих Oberon-реализациях выполнение такой операции приводит к ошибке времени выполнения. В данной реализации охрана типа нулевого указателя не приводит к ошибке, а проверка типа дает результат FALSE. В ряде случаев это позволяет значительно сократить частоту применения охраны типа. ------------------------------------------------------------------------------ Дополнительные стандартные процедуры COPY (x: ARRAY OF CHAR/WCHAR; VAR v: ARRAY OF CHAR/WCHAR); v := x; Если LEN(v) < LEN(x), то строка x будет скопирована не полностью LSR (x, n: INTEGER): INTEGER Логический сдвиг x на n бит вправо. MIN (a, b: INTEGER): INTEGER Минимум из двух значений. MAX (a, b: INTEGER): INTEGER Максимум из двух значений. BITS (x: INTEGER): SET Интерпретирует x как значение типа SET. Выполняется на этапе компиляции. LENGTH (s: ARRAY OF CHAR/WCHAR): INTEGER Длина 0X-завершенной строки s, без учета символа 0X. Если символ 0X отсутствует, функция возвращает длину массива s. s не может быть константой. WCHR (n: INTEGER): WCHAR Преобразование типа, аналогично CHR(n: INTEGER): CHAR ------------------------------------------------------------------------------ Импорт модулей с указанием пути и имени файла Примеры: IMPORT Math IN "./lib/math.ob07"; (* относительно текущего модуля *) IMPORT M1 IN "C:\lib\math.ob07"; (* абсолютный путь *) ------------------------------------------------------------------------------ Плавающая точка Компилятор предназначен для устройств на ядре Cortex-M3 и, возможно, после небольшой доработки, также для Cortex-M0. В таких микроконтроллерах нет встроенной поддержки плавающей точки, поэтому операции с вещественными числами одинарной точности эмулируются (модуль lib/STM32CM3/FPU.ob07). Компилятор подставляет вызовы процедур в месте операций с вещественными числами. Сохраняется возможность доработки компилятора в будущем для устройств со встроенной поддержкой вещественных чисел. ------------------------------------------------------------------------------ Использование регистров общего назначения R0 - R12 R0 - R3: регистровый стэк (промежуточные значения выражений), волатильные регистры (не требуют сохранения) R4 - R7: не используются компилятором, могут использоваться в кодовых процедурах, неволатильные (требуется сохранять перед использованием и восстанавливать после) R8 - R12: зарезервированы для возможного специального назначения в будущем ------------------------------------------------------------------------------ Вызов процедур и кадр стэка Правила вызова похожи на соглашение cdecl (x86): - параметры передаются через стэк справа налево - результат, если есть, передается через регистр R0 - вызывающая процедура очищает стэк Состояние стэка при выполнении процедуры: меньшие адреса <- |var3|var2|var1|LR|arg1|arg2|arg3| -> бОльшие адреса LR - сохраненный регистр LR (адрес возврата) argX - параметры в порядке объявления (слева направо) varX - локальные переменные в порядке использования в процедуре Размер каждого элемента в стэке (кроме локальных переменных структурных типов) - 1 машинное слово (4 байта). Структурные переменные (массивы и записи) занимают место в стэке в соответствии с их размером (с учетом выравнивания). Размещение локальных переменных зависит от их размеров и порядка использования, и в общем случае неопределенно. Если переменная не используется явно, то компилятор не выделяет для нее место в стэке. ------------------------------------------------------------------------------ Скрытые параметры процедур Некоторые процедуры могут иметь скрытые параметры, они отсутствуют в списке формальных параметров, но учитываются компилятором при трансляции вызовов. Это возможно в следующих случаях: 1. Процедура имеет формальный параметр открытый массив: PROCEDURE Proc (x: ARRAY OF ARRAY OF REAL); Вызов транслируется так: Proc(LEN(x), LEN(x[0]), SYSTEM.ADR(x)) 2. Процедура имеет формальный параметр-переменную типа RECORD: PROCEDURE Proc (VAR x: Rec); Вызов транслируется так: Proc(SYSTEM.TYPEID(Rec), SYSTEM.ADR(x)) ------------------------------------------------------------------------------ Кодовые процедуры Компилятор поддерживает процедуры, написаные в машинных кодах. Синтаксис: PROCEDURE "[code]" имя [ (параметры): ТипРезультата ] МашКом, МашКом,... МашКом; ";" после заголовка и END "имя" в конце процедуры не ставятся. МашКом - целочисленная константа [0..65535] (в том числе и константное выражение). Примеры: PROCEDURE [code] WFI 0BF30H; (* wfi *) (* сумма квадратов (a*a + b*b) -> r0 *) PROCEDURE [code] SqrSum (a, b: INTEGER): INTEGER 0B430H, (* push {r4, r5} *) (* сохранить все используемые регистры, кроме r0, r1, r2, r3 *) 09C02H, (* ldr r4, [sp, 8] *) (* r4 <- a *) 09D03H, (* ldr r5, [sp, 12] *) (* r5 <- b *) 04364H, (* muls r4, r4 *) (* r4 := r4 * r4 *) 0436DH, (* muls r5, r5 *) (* r5 := r5 * r5 *) 01960H, (* adds r0, r4, r5 *) (* r0 := r4 + r5; результат в r0 *) 0BC30H; (* pop {r4, r5} *) (* восстановить регистры *) Компилятор автоматически добавляет к такой процедуре команду возврата (bx LR). Способ передачи параметров и результата не изменяется. Регистр LR, при входе в процедуру не сохраняется. Чтобы использовать кодовые процедуры, необходимо импортировать псевдомодуль SYSTEM. ------------------------------------------------------------------------------ Обработка прерываний При возникновении прерывания, будет вызван обработчик (если он объявлен). Объявление обработчика: PROCEDURE handler_name [iv]; (* процедура без параметров *) iv - целочисленная константа (константное выражение), номер вектора прерывания в таблице векторов, iv >= 2: 0 начальное значение SP 1 сброс ... 15 SysTick ... 59 TIM6 60 TIM7 ... например: (* обработчик прерываний от TIM6 *) PROCEDURE tim6 [59]; BEGIN (* код обработки *) END tim6; Также, можно объявить общий обработчик (iv = 0), который будет вызван, если не назначен индивидуальный. Общий обработчик получает параметр - номер вектора прерывания. По значению этого параметра, обработчик должен определить источник прерывания и выполнить соответствующие действия: PROCEDURE handler (iv: INTEGER) [0]; BEGIN IF iv = 59 THEN (* TIM6 *) ELSIF iv = 60 THEN (* TIM7 *) ELSIF .... .... END END handler; ------------------------------------------------------------------------------ Обработка ошибок В случае возникновения ошибки при выполнении программы, будет вызван пользовательский обработчик (если он объявлен). Перед вызовом обработчика, будут запрещены прерывания. Объявление обработчика ошибок: PROCEDURE trap (modNum, modName, err, line: INTEGER) [1]; BEGIN END trap; где, modNum - номер модуля (в отчете о компиляции: compiling (modNum) "modName" ) modName - адрес имени модуля err - код ошибки line - номер строки Коды ошибок: 1 ASSERT(x), при x = FALSE 2 разыменование нулевого указателя 3 целочисленное деление на неположительное число 4 вызов процедуры через процедурную переменную с нулевым значением 5 ошибка охраны типа 6 нарушение границ массива 7 непредусмотренное значение выражения в операторе CASE 8 ошибка копирования массивов v := x, если LEN(v) < LEN(x) 9 CHR(x), если (x < 0) OR (x > 255) 10 WCHR(x), если (x < 0) OR (x > 65535) 11 неявное приведение x:INTEGER к v:BYTE, если (x < 0) OR (x > 255) После возврата из обработчика, программа будет перезапущена. ------------------------------------------------------------------------------