Rev 637 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download
ВведениеЯзык NedoLang - подмножество языка Си со строгой типизацией. Программа на NedoLang, оформленная по рекомендациям совместимости, может компилироваться компилятором Си при подключении файла nedodefs.h.Пакет NedoLang позволяет разрабатывать загружаемые программы (todo и ПЗУ) для Z80 и ARM Thumb без привлечения внешних утилит и библиотек, не считая эмуляторов/отладчиков. Кроме того, поскольку компилятор может компилировать сам себя, то можно работать непосредственно на Z80 (todo и на ARM Thumb).Как начать работу# Создайте в каталоге nedolang каталог своего проекта. Перейдите в этот каталог.# Создайте исходник главного модуля main.c. В нём напишите:PROC main(){}# Создайте файл стартапа main.s. В нём напишите:org 0x6000 ;адрес размещения программы для Z80 (для ARM см. инструкцию к вашему процессору)jp main ;переход на процедуру main()include "main.ast" ;код главного модуляinclude "main.var" ;переменные главного модуляinclude "../_sdk/lib.i" ;стандартная библиотека# Создайте скрипт компиляции compile.bat. В нём напишите (для Z80 - для ARM используется nedolarm, nedotarm, nedoaarm и нет стандартной библиотеки lib.i):@echo offpath=..\_sdk\echo ...compiling...nedolang main.ctype err.fecho ...tokenizing...nedotok main.s main.ast main.var ../_sdk/lib.iecho ...assembling...nedoasm main.S_type asmerr.fpauseУже можно проверять, что ваша программа компилируется - запускайте compile.bat и смотрите ошибки. Сейчас ошибок нет, показывается только lbls ... buf ... - количество меток и размер буфера под них. Ваша программа скомпилирована в main.bin.# Подключите необходимые библиотеки, например:В начале main.c:#include "../_sdk/print.h"В конце main.s:include "../_sdk/print.i" ;код библиотеки printВ compile.bat исправьте строчку:nedotok main.s main.ast main.var ../_sdk/lib.i ../_sdk/print.iМожно проверить, что программа компилируется с библиотеками.# Добавьте код в main():setxy(0x00,0x00);nprintf("Hello world!\n");# Добавьте скрипт запуска run.bat (для Z80):call compile.batnedotrd test.trd -nnedotrd test.trd -s 24576 -ac main.bin..\..\emul\emul.exe test.trdПредполагается, что эмулятор emul.exe лежит в каталоге ..\..\emul\ (т.е. в соседнем с каталогом nedolang).Теперь у вас на диске test.trd есть запускаемый кодовый файл main.C с адресом 24576 и открывается эмулятор.# В ..\batch\basics.trd есть бейсик-загрузчик boot, который загружает и запускает main.C с адресом 24576. Добавьте в свой run.bat перед запуском эмулятора:nedotrd ..\batch\basics.trd -eh boot.$bnedotrd test.trd -ah boot.$bТеперь можно запускать программу простым нажатием Enter в эмуляторе (можно даже настроить автозапуск).# Можете удалить временные файлы (*.ast, *.var, *.org, *.pst, ошибки и токенизированные ассемблерные тексты) утилитой clean.bat в корневом каталоге nedolang.Если когда-нибудь понадобится подключить самодельный ассемблерный файл, назначьте ему расширение *.s, чтобы утилита clean.bat его не удалила.Описание языкаЯзык в текущей версии не обеспечивает аналогов следующих конструкций Си:- многомерные массивы (можно взамен использовать вычисления индекса или массивы указателей);- числа с плавающей точкой (todo!);- sizeof(<expr>) - есть только +sizeof(<type>), +sizeof(<var>) не для массивов;- sizeof нельзя использовать в константах;- #include <filename> (есть только #include "filename");- макросы #define (нерекомендуемое поведение, есть только константы #define);- for (следует взамен использовать while или repeat);- статические структуры и массивы структур (сейчас доступ к структурам только по указателю);- вложенные структуры (todo!);- объединения (нерекомендуемое поведение);- копирование константных строк через =;- выход из середины функции по return (нерекомендуемое поведение);- выход из середины программы по exit (нерекомендуемое поведение);- вызов функции по указателю с параметром и возвращаемым значением (сейчас можно вызывать по указателю только процедуры без параметров);- тип указателя на указатель (можно обойти, т.к. тип указателя указывается при разыменовании - *(<poitype>)poi; poke *(<poitype>)(<poiexpr>) = <expr>).Команды собираются в блоки, окружённые фигурными скобками {<command><command>...<command>}.Такой блок эквивалентен одиночной команде и допустим везде, где допустима одиночная команда.После каждой команды для совместимости с Си рекомендуется (но не требуется) ставить точку с запятой.В одной строке может быть сколько угодно команд. Перевод строки между любыми словами ни на что не влияет.Номера строк в исходном тексте не пишутся. Поэтому при ошибках выводится номер строки исходного текстового файла (первая строка имеет номер 1).Стандартные комментарии оформляются как /**комментарий*/. Если впереди только одна звёздочка, то это не комментарий.Стандартные комментарии не могут быть вложенными.Есть также стандартные однострочные комментарии от // до конца строки.Нестандартные однострочные комментарии: от ;; до конца строки.Также игнорируются строки, которые начинаются с #, кроме реализованных команд с # (см. ниже).Комментарии можно ставить между любыми словами, кроме следующих случаев:- после первого слова команды (даже для команд, определяемых по формату, даже между меткой и двоеточием и между <varname> и знаком '=');- внутри размера массива при его объявлении, то есть так нельзя: var int a[100/**комментарий*/];- между именем переменной и квадратной скобкой индекса массива, то есть так нельзя: a/**комментарий*/[10].- между именем типа и *.Все комментарии при компиляции передаются в ассемблерный текст тоже в виде комментариев (если задана соответствующая опция компилятора, см. ниже).Полное имя переменной (которое используется в ассемблере) строится из имени текущей области видимости (в текущей версии пустое), имени самой переменной и постфикса.Полное имя "__sys._petyamodule._pet" означает: область видимости "__sys._petyamodule", переменная "_pet", заданная в исходном тексте (нет точки в конце).Полное имя "__globalarr" означает: глобальная переменная "__globalarr", заданная в исходном тексте (нет точки в конце).Полное имя "__sys.mainproc.A." означает: область видимости "__sys.mainproc", переменная "A.", созданная автоматически (отмечено точкой ".").Таким образом, автоматически созданные переменные не пересекаются по именам с переменными, заданными в исходном тексте.Так же строятся полные имена заголовков функций (без точки в конце) и меток для перехода (пользовательские без точки, автоматические с точкой в конце).Переменные и прочие имена доступны из языка следующим образом:i для доступа к переменной i, определённой в текущей области видимости (то есть в текущей функции) или к глобальной переменной.func2() для доступа к функции func2(), определённой снаружи текущей области видимости (то есть в текущем модуле).//_submodule._v для доступа к переменной _v, определённой в дочерней области видимости _submodule.//Не имеет смысла называть модули без подчёркивания - они будут недоступны из внешнего модуля.//_submodule.proc2() для доступа к процедуре proc2(), определённой в дочерней области видимости _submodule.//__sys._anothermodule.func5() для доступа к функции func5(), определённой в модуле _anothermodule, включенном в глобальный модуль __sys.Автоматически созданные переменные недоступны из языка.Идентификаторы (имена переменных, процедур, функций, меток) должны начинаться с буквы (или знака подчёркивания для глобальных переменных и глобальных процедур/функций) и состоять из букв, цифр и знаков подчёркивания. Полное имя переменной не должно быть длиннее 79 знаков. Разрешается использовать русские буквы в идентификаторах. Русские буквы остаются в том же виде, как они закодированы в исходном тексте (в кодировке cp866, cp1251 или utf8).Имеются следующие типы данных:* BYTE (имеет размер 1 байт, без знака)* BOOL (допустимы только значения +TRUE и +FALSE, размер не регламентируется)* CHAR (имеет размер 1 байт, знаковость не регламентируется)* INT (знаковое целое, имеет размер в одно слово процессора)* UINT (беззнаковое целое, имеет размер в одно слово процессора)* LONG (беззнаковое длинное целое, имеет размер в два слова процессора)[* FLOAT (пока клон LONG, кроме таргета Script - там работает как double)]* PBYTE,PBOOL,PCHAR,PINT,PUINT,PLONG,[PFLOAT], <typename>* (указатель - имеет размер в одно слово процессора)* STRUCT <structname> (структура <structname> - только для константных структур и +sizeof(STRUCT <structname>))* STRUCT <structname>* (указатель на структуру <structname>)Тип BOOL в текущей версии имеет размер 1 байт, логические значения следует писать +TRUE и +FALSE (в текущей реализации равны 0xff и 0x00, но не рекомендуется смешивать логические значения с числовыми).Любое значение, отличное от FALSE, воспринимается как TRUE, но не рекомендуется использовать этот факт для будущей совместимости.Узнать размер типа, переменной простого типа или структуры в байтах можно с помощью +sizeof(<type>). Выражение +sizeof(<type>*) выдаёт размер указателя.Разрешается использовать необъявленные константы в форме +<variable>, если имя начинается со знака подчёркивания '_'. В этом случае константа получает тип BYTE (иначе BOOL). Рекомендуется использовать такую запись только для констант из enum.В выражениях строго проверяются типы. В любой операции с двумя операндами типы операндов должны совпадать.Приведение типов выглядит так:(<type>)<value>Поскольку наш язык не допускает разные типы в одном операторе, то для смещения указателя на N байт вперёд используется выражение:(<type>)((UINT)<pointername> + N)Или для PBYTE:&<pointername>[N]Для получения указателя на массив используется выражение:(PCHAR)<arrayname>При использовании вызова функции в качестве параметра надо ставить правильный тип указателя (указатель не равен массиву, в отличие от Си).Чтение по указателю допускается только с непосредственным приведением типа: *(<pointertype>)<pointervalue>Вызов функций оформляется как <procname>([<expression>,...]).Процедуры и функции технически имеют плавающее число параметров, но не рекомендуется использовать это поведение.В выражениях <expression> имеются следующие приоритеты операций:1. Индексные квадратные скобки: arr[] или pointer[]2. Префиксы (+, -, ~ (инверсия), ! (логическое отрицание), & (взятие адреса переменной &<variable>, процедуры/функции &<func>, элемента массива &<array>[<expression>], поля структуры &(<structname>-><field>)), * (чтение по указателю)).3. Умножение, деление, &, &&.4. Сложение, вычитание, |, ^, ||, -> (адресация элемента структуры по указателю).5. Сравнения и сдвиги (<<, >>). Сравнения и сдвиги работают только для типов BYTE, CHAR, INT, UINT. Сдвиги - также для LONG. Для BOOL разрешены только сравнения на равенство и неравенство. Сравнение на равенство можно писать == или =, но второй вариант несовместим с Си.В выражениях допустимы следующие виды значений:* идентификатор переменной - получает тип по типу переменной.* целая числовая константа без знака - получает тип BYTE (если запись в стиле 0xff с не более чем 2 цифрами или в стиле 0b111 с не более чем 8 цифрами), LONG (если в конце стоит L) или UINT (в остальных случаях).* целая числовая константа со знаком (+ или -) - получает тип INT.* числовая константа с плавающей точкой - получает тип FLOAT (который пока не поддерживается).* символьная константа 'c' или '\<символ>' (допустимы только '\n', '\r', '\t', '\0', '\'', '\"', '\\'), где <символ> - один символ - получает тип char.* строковая константа "строка" - получает тип PCHAR. Допускается запись строковых констант в виде "str1""str2" (между ними допустим перевод строки) для наглядности и для избегания переполнения буфера строки (79 символов) в компиляторе. Строковые константы создаются автоматически с нулевым кодом в конце.* константное выражение +(expr) - получает тип по левому контексту.Целые числовые константы могут быть десятичные (100), шестнадцатеричные (0x10), двоичные (0b11), восьмеричные (0o177 или как вариант 0177 с выдачей предупреждения из-за двусмысленности записи).Команды, которые определяются по формату:* <labelname><:> - определить метку для перехода. Метка должна быть уникальной внутри текущей области видимости. Подчёркивание добавлено, чтобы метку было лучше видно в тексте программы.* <label>([<expression>,...]) - вызвать процедуру. Нельзя ставить пробел перед открывающей скобкой (ср. формат if).* <var>=<expression> - вычислить и записать в переменную. Тип выражения должен соответствовать типу переменной. Получить в указатель адрес массива можно так: poi=(PBYTE)arr.* <var><[><expression><]>=<expression> - вычислить и записать в ячейку массива. Таким же образом можно писать в массив, адрес которого передан как указатель. Будьте осторожны, границы массива не проверяются!Доступ к ячейкам массива на чтение делается так же: <var><[><expression><]> (в том числе если адрес массива передан как указатель).* <var>-><field>=<expression> - вычислить и записать в поле структуры (которая лежит по указателю <var>). Допускаются цепочки ->.Команды, которые начинаются с ключевого слова:* const - определить константу: const<type><variable>[=<constnum>], где <constnum>::=[-|+]<num>|'<char>'|"<string>"["<string>"...] или для массивов/структур: const<type><variable><[><expr><]>={<constnum>[,<constnum>...]}Пропуск значения или списка значений нужен для использования внешних констант (не из текущего компилируемого модуля). Список значений для массива должен соответствовать числу значений в массиве (не проверяется).Нельзя определять два раза одну и ту же константу со значением (TODO проверять в ассемблере). См. выше про необъявленные константы.* extern - описать тип внешней переменной: extern<type><variable> - допускается только снаружи процедур и функций (не проверяется).* var - определить переменную: var<type><variable>[=<expression>] - если с присваиванием, то допускается только внутри процедур или функций (не проверяется). Это именно присваивание, а не определение начального значения переменной. Нельзя присваивать массивы (не проверяется). В рекурсивных процедурах и функциях присваивание в var запрещено (не проверяется), а после блока всех var всё последующее тело функции должно быть в фигурных скобках {} (не проверяется), иначе переменные не будут правильно восстанавливаться при рекурсии.* var с квадратными скобками - определить массив фиксированного размера: var<type><variable><[><expression><]> - ячейки массива нумеруются с 0, то есть в массиве a[10] не существует ячейки с индексом 10. Индекс должен иметь тип byte или uint.* enum - определить последовательный ряд констант: enum{<enumconstname>[=<number>],<enumconstname>[=<number>]...} - первая константа получит значение 0, вторая 1 и т.д., =<number> меняет этот счётчик. Эти константы невидимы как переменные, поэтому для использования их надо либо объявить через const, либо использовать +<enumconstname> (см. выше про необъявленные константы). Разрешена запятая после последнего элемента.* evar {UINT var1 = 1, INT var2 = 2, FLOAT var3} и т.п. создаёт переменные заданого типа с заданными адресами.* poke* - вычислить и записать в память с нужным типом: poke*<pointervalue>=<expression> - ключевое слово нужно, чтобы можно было пропускать ; в конце операторов.[* module - определить модуль (область видимости): module<label><command> - команда <command> создаётся в области видимости внутри текущей области видимости (например, если была область видимости mainmodule, то внутри команды "module submodule{...}" будет область видимости mainmodule.submodule. Можно повторно определять одну и ту же область видимости, чтобы добавлять туда что-то новое.]* proc - определить процедуру: proc<procname>[recursive][forward]([<type><par>,...])[<command>] - тоже создаёт область видимости внутри текущей области видимости. Поэтому <procname> должно быть уникальным внутри текущей области видимости. Если есть слово forward, то тело процедуры/функции не создаётся, и <command> не нужна (используется при использовании внешних процедур/функций или если их тело описано ниже вызова). Если есть слово recursive, то локальные переменные сохраняются при входе-выходе в процедуру/функцию, а при вызове сохраняются старые значения параметров. Внутри рекурсивной процедуры/функции нельзя объявлять массивы (не проверяется).* func - определить функцию: func<type><funcname>[recursive][forward]([<type><par>,...])[<command>] - тоже создаёт область видимости внутри текущей области видимости. Поэтому <funccname> должно быть уникальным внутри текущей области видимости. См. выше про слова recursive и forward.* if - альтернатива: if (<boolexpression>) <command>[else<command>]; - ';' против ошибки "if (expr); cmd" и против ошибки вложенных неполных альтернатив. Пробел после if обязателен (ср. формат вызова процедуры). При вложенных if достаточно одного ';'.*TODO ifnot*TODO ifz*TODO ifnz* while - цикл с предусловием: while (<boolexpression>) <command>[;] - ';' против ошибки "while(expr);cmd".*TODO whilenot*TODO whilez*TODO whilenz* repeat - цикл с постусловием: repeat<command>until (<boolexpression>) - если сделать скобки необязательными, то не получится определить "логическое выражение верхнего уровня", которое можно оставить во флаге.*TODO untilnot*TODO untilz*TODO untilnz* break - выйти из цикла while или repeat или из блока switch: параметров не имеет, просто break.* return - вернуть значение из функции: return<expression> - должна быть последней командой в функции. Тип возвращаемого значения должен соответствовать типу функции.* goto - перейти на метку: goto<labelname> - разрешается только внутри текущей процедуры или функции.* call - вызвать процедуру по указателю: call (<expression>) - тип выражения - любой указатель или UINT (это не проверяется).* asm - ассемблерная вставка: asm(" cmd1"" cmd2""label1"...) - каждая команда генерируется как отдельная строка. Нельзя писать команды без пробелов или табуляций вначале - токенизатор и ассемблер их не поймут.* inc - увеличить значение переменной на единицу: inc <var>* dec - уменьшить значение переменной на единицу: dec <var>* struct - объявить структуру: struct<structname>{<type1><field1>[;]<type2><field2>[;]...} - допускается использование в структуре указателей на структуры, объявленные выше, и на саму себя (для совместимости с Си надо в объявлении структуры поля, являющиеся указателями на структуры, предварять словом struct). Потом можно объявить указатель на структуру: var struct <structname>* <structpointername> - и использовать поля структуры: <structpointername>-><field>* switch - блок множественного ветвления по значению типа byte:switch (<byteexpr>){case <byteconst>: [<commands>]case <byteconst>: [<commands>]...default: [<commands>]};Разрешается только один switch в процедуре/функции. Все названия веток <byteconst> должны быть описаны в const или enum. Поля "case <byteconst>:" и "default:" (ветка по умолчанию) эквивалентны меткам (label), поэтому они могут стоять в произвольных местах, а автоматический выход из веток не предусмотрен. Для этого можно использовать goto или break (break здесь выходит из switch).* #include "filename" - вложенно включить текст из другого файла. Используется для подключения внешних определений (дублирования на данный момент запрещены).* #define <constname> <value> или #define <costname> (<type>)(<expr>) - определить константу совместимо с Си. В отличие от const, можно преопределять. Требуется для констант в заголовочных файлах и констант размера массивов, можно и для других случаев.# #undef <constname> - удалить ранее определённую константу (или любой другой идентификатор, но это будет несовместимо с Си).* условная компиляция (разрешается вложенная): #ifdef <defined_const> ... [#else] ... #endif или #ifndef <defined_const> ... [#else] ... #endif.* export - ставится перед определением (процедуры/функции, переменной, константного массива/структуры), смещение адреса которого нужно экспортировать в файл filename.D_.* typedef <type> <typename> - определить новый тип на основе старого. Для структурных типов надо писать слева слово struct для совместимости с Си.Все команды с # должны начинаться с начала строки и занимать всю строку.В условиях (if, while, until) имеется оптимизация сравнений за счёт проверки, что они осуществляются на 1-м уровне вложенности выражения (скобки не учитываются - они являются частью синтаксиса команды). Поэтому на этом же уровне нельзя писать другие операции (например, нельзя a==b==c, можно (a==b)==c). Это не проверяется?В вычислениях (присваивание, var, return, параметры вызовов) сравнения можно писать только в скобках, чтобы они не оказались на 1-м уровне вложенности (например, нельзя a = b==c, можно a = (b==c)).Рекомендуемый стиль оформления условий:IF (cond1) do1();IF (cond2) {do2();}ELSE IF (cond3) {if (cond4) {do3();}ELSE do4();};IF (cond5) {do5();}ELSE IF (cond6) {do6();}ELSE {do7();};Рекомендуется все ключевые слова языка писать большими буквами, а в текущей версии - типы и STRUCT обязательно большими буквами.Чтобы скомпилировать такой же исходник с помощью компилятора Си, нужно использовать #include "nedodefs.h".Такой синтаксис - один из вариантов, получившихся из следующих требований:- при анализе команд использовать только левый контекст;- любая команда определяется без поиска по меткам (зарезервированных слов нет);- при этом должна обеспечиваться совместимость с компилятором Си.Структура проектаПроект состоит из модулей на NedoLang (могут включать другие модули через #include) и на ассемблере (могут включать другие ассемблерные модули через include). Модули по одному передаются в компилятор (кроме включаемых через #include), потом все ассемблерные файлы надо токенизировать, потом надо вызвать NedoAsm с именем стартап-файла *.s.Стартап-файл - ассемблерный файл с запускающим кодом, должен включать (через include "filename.ast") остальные файлы проекта. В реальности вместо filename.ast будет грузиться filename.A_ и т.п.nedolang.exe - компилятор, имеет следующие параметры:filename1.c filename2.c ... - компилировать перечисленные модули по одному.-C - включить выдачу комментариев (для компиляции).-H - включить выдачу подробного лога (для компиляции).Выходные файлы компилятора:*.ast - ассемблерный текст с кодом*.var - ассемблерный текст с даннымиerr.f - ошибки компиляции[label.f - метки во внутреннем формате]nedotok.exe - токенизатор, вызывается так:nedotok filename1.ast filename2.var filename3.s filename4.i ...(выходные файлы будут называться filename1.A_, filename2.V_ и т.п.).nedoasm.exe - ассемблер, вызывается так:nedoasm filename.A_Выходные файлы ассемблера:filename.bin - все сгенерированные байты кода и данных подрядfilename.org - данные релокации (в формате: 2 байта смещение, где лежит релоцируемый адрес)[label0.f..label7.f - метки во внутреннем формате]filename.pst - пост-метки во внутреннем формате (пока не поддерживается)asmerr.f - ошибки ассемблированияfilename.D_ - токенизированный файл деклараций (команды export label в ассемблере)Ассемблер выгружает код подряд, поэтому допустим только один ORG - начальный. Выравнивать данные можно через DS -$&mask.nedoexp.exe - детокенизатор (отладочный экспорт токенизированного ассемблера), вызывается так:nedoexp filename.A_(выходной файл exp.f)Библиотеки:- Ассемблерный файл lib.i содержит стандартные математические функции (умножение, деление, сдвиги) для Z80.- Заголовочный файл str.h содержит функции работы со строками. Для его использования подключайте в стартапе под Z80 str.i (или же вместо него str.c компилируйте в str.ast, str.var и подключайте их в стартапе).- Заголовочный файл io.h одержит функции работы с файлами. Для его использования подключайте в стартапе под Z80 iofast.i под TR-DOS или io_os.i под NedoOS (или же вместо него io.c под TR-DOS без функций низкого уровня компилируйте в io.ast, io.var и подключайте их в стартапе).- runtime.h, runtime.i - необязательная рантайм-библиотека для Z80 (прерывания, порты).- sprite.h, sprite.i - спрайтовая библиотека для Z80.- print.h, print.i (print_os.i для NedoOS) - библиотека вывода текста для Z80 (setxy, prchar, nprintf).Утилиты для Windows:- nedotrd - работа с образом диска в формате TRD. Можно перевести на NedoLang, если в файловой библиотеке поддержать ftell(pos)/fseek.- nedores - конвертор графических ресурсов. Пока затруднительно перевести на NedoLang из-за размера массива.- nedopad - обрезка или дополнение файла до заданного размера. Можно перевести на NedoLang.- _gui - простейший интерфейс ввода и компиляции исходника на NedoLang (можно с автоматической перекомпиляцией в процессе ввода) с просмотром ошибок и результата в ассемблерном виде.Утилиты на NedoLang:- nedodefb - перевод файла в ассемблерное шестнадцатеричное представление с обрезкой или дополнением (пока только для Z80). Можно перевести на NedoLang. Необязательно при использовании предыдущей утилиты.- nedodiff - сравнение двух файлов (пока только для Z80, т.к. использует print.i).- batch - интерпретатор командных файлов (пока только для Z80, лежит по адресу 64000).- nedodel - удаление файла.- movedisk - уплотнение образа диска после удаления файлов.- diff - сравнение двух файлов.При указании путей в #include, include и в командной строке надо использовать прямую косую черту '/'.Результат компиляции кладётся в ту же директорию, где лежит исходный файл.Формат ассемблерного текста зависит от архитектуры целевого процессора.Поддерживаются директивы:- db <byte>,<byte>... (dcb для ARM Thumb) - скомпилировать байты- dw <word>,<word>... (dcd для ARM Thumb) - скомпилировать слова- dl <long>,<long>... (dcq для ARM Thumb) - скомпилировать двойные слова- ds <size>[,<byte>] (space для ARM Thumb) - скомпилировать пустой блок заданного размера, можно 0- org <addr> - указать адрес размещения программы, с которого она будет работать- align <2^n> (для ARM Thumb) - пропустить байты для выравнивания адреса размещения на адрес, кратный параметру (того же можно добиться с помощью ds -$&<2^n-1>- end (для ARM Thumb - ничего не делает)- include "filename.ast" - подключить исходник с заданным именем. Фактически подключается токенизированный filename.A_. Разрешается вложенность- incbin "filename" - подключить бинарный файл- <label>=<expression> - присвоить метку (можно переприсвоить)- <label> equ <expression> - присвоить метку (можно переприсвоить - todo запретить)- export <label> - выгрузить значение метки в файл filename.D_ (токенизированный) в виде <label>=_+<labelvalue>. Этот файл можно включать в ассемблерный текст через include "filename.dcl".- todo disp- todo entТекущий адрес компиляции можно получить с помощью знака $.Определяемая метка должна стоять в начале строки.Разрешается писать несколько команд в строке через пробел (через двоеточие пока не работает).Ассемблер двухпроходный. Кодогенерация происходит на втором проходе. На момент генерации каждой строчки уже должны быть вычислены все используемые метки, иначе выводится ошибка.dw $,$ работает как dw $ dw $