Rev 637 | Details | Compare with Previous | Last modification | View Log
Rev | Author | Line No. | Line |
---|---|---|---|
230 | alone | 1 | Введение |
8 | dimkam | 2 | |
230 | alone | 3 | Язык NedoLang - подмножество языка Си со строгой типизацией. Программа на NedoLang, оформленная по рекомендациям совместимости, может компилироваться компилятором Си при подключении файла nedodefs.h. |
8 | dimkam | 4 | |
230 | alone | 5 | Пакет NedoLang позволяет разрабатывать загружаемые программы (todo и ПЗУ) для Z80 и ARM Thumb без привлечения внешних утилит и библиотек, не считая эмуляторов/отладчиков. Кроме того, поскольку компилятор может компилировать сам себя, то можно работать непосредственно на Z80 (todo и на ARM Thumb). |
8 | dimkam | 6 | |
230 | alone | 7 | Как начать работу |
8 | dimkam | 8 | |
230 | alone | 9 | # Создайте в каталоге nedolang каталог своего проекта. Перейдите в этот каталог. |
8 | dimkam | 10 | |
230 | alone | 11 | # Создайте исходник главного модуля main.c. В нём напишите: |
8 | dimkam | 12 | PROC main() |
13 | { |
||
14 | } |
||
15 | |||
230 | alone | 16 | # Создайте файл стартапа main.s. В нём напишите: |
17 | org 0x6000 ;адрес размещения программы для Z80 (для ARM см. инструкцию к вашему процессору) |
||
18 | jp main ;переход на процедуру main() |
||
19 | include "main.ast" ;код главного модуля |
||
20 | include "main.var" ;переменные главного модуля |
||
21 | include "../_sdk/lib.i" ;стандартная библиотека |
||
8 | dimkam | 22 | |
230 | alone | 23 | # Создайте скрипт компиляции compile.bat. В нём напишите (для Z80 - для ARM используется nedolarm, nedotarm, nedoaarm и нет стандартной библиотеки lib.i): |
8 | dimkam | 24 | @echo off |
25 | path=..\_sdk\ |
||
26 | |||
27 | echo ...compiling... |
||
28 | nedolang main.c |
||
29 | type err.f |
||
30 | |||
31 | echo ...tokenizing... |
||
20 | dimkam | 32 | nedotok main.s main.ast main.var ../_sdk/lib.i |
8 | dimkam | 33 | |
34 | echo ...assembling... |
||
35 | nedoasm main.S_ |
||
36 | type asmerr.f |
||
37 | |||
38 | pause |
||
39 | |||
230 | alone | 40 | Уже можно проверять, что ваша программа компилируется - запускайте compile.bat и смотрите ошибки. Сейчас ошибок нет, показывается только lbls ... buf ... - количество меток и размер буфера под них. Ваша программа скомпилирована в main.bin. |
8 | dimkam | 41 | |
230 | alone | 42 | # Подключите необходимые библиотеки, например: |
43 | В начале main.c: |
||
8 | dimkam | 44 | #include "../_sdk/print.h" |
230 | alone | 45 | В конце main.s: |
46 | include "../_sdk/print.i" ;код библиотеки print |
||
47 | В compile.bat исправьте строчку: |
||
20 | dimkam | 48 | nedotok main.s main.ast main.var ../_sdk/lib.i ../_sdk/print.i |
8 | dimkam | 49 | |
230 | alone | 50 | Можно проверить, что программа компилируется с библиотеками. |
8 | dimkam | 51 | |
230 | alone | 52 | # Добавьте код в main(): |
8 | dimkam | 53 | setxy(0x00,0x00); |
54 | nprintf("Hello world!\n"); |
||
55 | |||
230 | alone | 56 | # Добавьте скрипт запуска run.bat (для Z80): |
8 | dimkam | 57 | call compile.bat |
58 | nedotrd test.trd -n |
||
59 | nedotrd test.trd -s 24576 -ac main.bin |
||
60 | ..\..\emul\emul.exe test.trd |
||
61 | |||
230 | alone | 62 | Предполагается, что эмулятор emul.exe лежит в каталоге ..\..\emul\ (т.е. в соседнем с каталогом nedolang). |
8 | dimkam | 63 | |
230 | alone | 64 | Теперь у вас на диске test.trd есть запускаемый кодовый файл main.C с адресом 24576 и открывается эмулятор. |
8 | dimkam | 65 | |
230 | alone | 66 | # В ..\batch\basics.trd есть бейсик-загрузчик boot, который загружает и запускает main.C с адресом 24576. Добавьте в свой run.bat перед запуском эмулятора: |
8 | dimkam | 67 | nedotrd ..\batch\basics.trd -eh boot.$b |
68 | nedotrd test.trd -ah boot.$b |
||
69 | |||
230 | alone | 70 | Теперь можно запускать программу простым нажатием Enter в эмуляторе (можно даже настроить автозапуск). |
8 | dimkam | 71 | |
230 | alone | 72 | # Можете удалить временные файлы (*.ast, *.var, *.org, *.pst, ошибки и токенизированные ассемблерные тексты) утилитой clean.bat в корневом каталоге nedolang. |
8 | dimkam | 73 | |
230 | alone | 74 | Если когда-нибудь понадобится подключить самодельный ассемблерный файл, назначьте ему расширение *.s, чтобы утилита clean.bat его не удалила. |
8 | dimkam | 75 | |
230 | alone | 76 | Описание языка |
8 | dimkam | 77 | |
230 | alone | 78 | Язык в текущей версии не обеспечивает аналогов следующих конструкций Си: |
79 | - многомерные массивы (можно взамен использовать вычисления индекса или массивы указателей); |
||
80 | - числа с плавающей точкой (todo!); |
||
81 | - sizeof(<expr>) - есть только +sizeof(<type>), +sizeof(<var>) не для массивов; |
||
82 | - sizeof нельзя использовать в константах; |
||
83 | - #include <filename> (есть только #include "filename"); |
||
84 | - макросы #define (нерекомендуемое поведение, есть только константы #define); |
||
85 | - for (следует взамен использовать while или repeat); |
||
86 | - статические структуры и массивы структур (сейчас доступ к структурам только по указателю); |
||
87 | - вложенные структуры (todo!); |
||
88 | - объединения (нерекомендуемое поведение); |
||
89 | - копирование константных строк через =; |
||
90 | - выход из середины функции по return (нерекомендуемое поведение); |
||
91 | - выход из середины программы по exit (нерекомендуемое поведение); |
||
92 | - вызов функции по указателю с параметром и возвращаемым значением (сейчас можно вызывать по указателю только процедуры без параметров); |
||
93 | - тип указателя на указатель (можно обойти, т.к. тип указателя указывается при разыменовании - *(<poitype>)poi; poke *(<poitype>)(<poiexpr>) = <expr>). |
||
8 | dimkam | 94 | |
230 | alone | 95 | Команды собираются в блоки, окружённые фигурными скобками {<command><command>...<command>}. |
96 | Такой блок эквивалентен одиночной команде и допустим везде, где допустима одиночная команда. |
||
97 | После каждой команды для совместимости с Си рекомендуется (но не требуется) ставить точку с запятой. |
||
8 | dimkam | 98 | |
230 | alone | 99 | В одной строке может быть сколько угодно команд. Перевод строки между любыми словами ни на что не влияет. |
100 | Номера строк в исходном тексте не пишутся. Поэтому при ошибках выводится номер строки исходного текстового файла (первая строка имеет номер 1). |
||
8 | dimkam | 101 | |
230 | alone | 102 | Стандартные комментарии оформляются как /**комментарий*/. Если впереди только одна звёздочка, то это не комментарий. |
103 | Стандартные комментарии не могут быть вложенными. |
||
104 | Есть также стандартные однострочные комментарии от // до конца строки. |
||
105 | Нестандартные однострочные комментарии: от ;; до конца строки. |
||
106 | Также игнорируются строки, которые начинаются с #, кроме реализованных команд с # (см. ниже). |
||
107 | Комментарии можно ставить между любыми словами, кроме следующих случаев: |
||
108 | - после первого слова команды (даже для команд, определяемых по формату, даже между меткой и двоеточием и между <varname> и знаком '='); |
||
109 | - внутри размера массива при его объявлении, то есть так нельзя: var int a[100/**комментарий*/]; |
||
110 | - между именем переменной и квадратной скобкой индекса массива, то есть так нельзя: a/**комментарий*/[10]. |
||
111 | - между именем типа и *. |
||
112 | Все комментарии при компиляции передаются в ассемблерный текст тоже в виде комментариев (если задана соответствующая опция компилятора, см. ниже). |
||
8 | dimkam | 113 | |
230 | alone | 114 | Полное имя переменной (которое используется в ассемблере) строится из имени текущей области видимости (в текущей версии пустое), имени самой переменной и постфикса. |
115 | Полное имя "__sys._petyamodule._pet" означает: область видимости "__sys._petyamodule", переменная "_pet", заданная в исходном тексте (нет точки в конце). |
||
116 | Полное имя "__globalarr" означает: глобальная переменная "__globalarr", заданная в исходном тексте (нет точки в конце). |
||
117 | Полное имя "__sys.mainproc.A." означает: область видимости "__sys.mainproc", переменная "A.", созданная автоматически (отмечено точкой "."). |
||
118 | Таким образом, автоматически созданные переменные не пересекаются по именам с переменными, заданными в исходном тексте. |
||
119 | Так же строятся полные имена заголовков функций (без точки в конце) и меток для перехода (пользовательские без точки, автоматические с точкой в конце). |
||
8 | dimkam | 120 | |
230 | alone | 121 | Переменные и прочие имена доступны из языка следующим образом: |
122 | i для доступа к переменной i, определённой в текущей области видимости (то есть в текущей функции) или к глобальной переменной. |
||
123 | func2() для доступа к функции func2(), определённой снаружи текущей области видимости (то есть в текущем модуле). |
||
124 | //_submodule._v для доступа к переменной _v, определённой в дочерней области видимости _submodule. |
||
125 | //Не имеет смысла называть модули без подчёркивания - они будут недоступны из внешнего модуля. |
||
126 | //_submodule.proc2() для доступа к процедуре proc2(), определённой в дочерней области видимости _submodule. |
||
127 | //__sys._anothermodule.func5() для доступа к функции func5(), определённой в модуле _anothermodule, включенном в глобальный модуль __sys. |
||
8 | dimkam | 128 | |
230 | alone | 129 | Автоматически созданные переменные недоступны из языка. |
8 | dimkam | 130 | |
230 | alone | 131 | Идентификаторы (имена переменных, процедур, функций, меток) должны начинаться с буквы (или знака подчёркивания для глобальных переменных и глобальных процедур/функций) и состоять из букв, цифр и знаков подчёркивания. Полное имя переменной не должно быть длиннее 79 знаков. Разрешается использовать русские буквы в идентификаторах. Русские буквы остаются в том же виде, как они закодированы в исходном тексте (в кодировке cp866, cp1251 или utf8). |
8 | dimkam | 132 | |
230 | alone | 133 | Имеются следующие типы данных: |
637 | alone | 134 | * BYTE (имеет размер 1 байт, без знака) |
135 | * BOOL (допустимы только значения +TRUE и +FALSE, размер не регламентируется) |
||
136 | * CHAR (имеет размер 1 байт, знаковость не регламентируется) |
||
137 | * INT (знаковое целое, имеет размер в одно слово процессора) |
||
138 | * UINT (беззнаковое целое, имеет размер в одно слово процессора) |
||
139 | * LONG (беззнаковое длинное целое, имеет размер в два слова процессора) |
||
140 | [* FLOAT (пока клон LONG, кроме таргета Script - там работает как double)] |
||
141 | * PBYTE,PBOOL,PCHAR,PINT,PUINT,PLONG,[PFLOAT], <typename>* (указатель - имеет размер в одно слово процессора) |
||
142 | * STRUCT <structname> (структура <structname> - только для константных структур и +sizeof(STRUCT <structname>)) |
||
143 | * STRUCT <structname>* (указатель на структуру <structname>) |
||
8 | dimkam | 144 | |
637 | alone | 145 | Тип BOOL в текущей версии имеет размер 1 байт, логические значения следует писать +TRUE и +FALSE (в текущей реализации равны 0xff и 0x00, но не рекомендуется смешивать логические значения с числовыми). |
230 | alone | 146 | Любое значение, отличное от FALSE, воспринимается как TRUE, но не рекомендуется использовать этот факт для будущей совместимости. |
8 | dimkam | 147 | |
230 | alone | 148 | Узнать размер типа, переменной простого типа или структуры в байтах можно с помощью +sizeof(<type>). Выражение +sizeof(<type>*) выдаёт размер указателя. |
8 | dimkam | 149 | |
637 | alone | 150 | Разрешается использовать необъявленные константы в форме +<variable>, если имя начинается со знака подчёркивания '_'. В этом случае константа получает тип BYTE (иначе BOOL). Рекомендуется использовать такую запись только для констант из enum. |
8 | dimkam | 151 | |
230 | alone | 152 | В выражениях строго проверяются типы. В любой операции с двумя операндами типы операндов должны совпадать. |
8 | dimkam | 153 | |
230 | alone | 154 | Приведение типов выглядит так: |
8 | dimkam | 155 | (<type>)<value> |
156 | |||
230 | alone | 157 | Поскольку наш язык не допускает разные типы в одном операторе, то для смещения указателя на N байт вперёд используется выражение: |
8 | dimkam | 158 | (<type>)((UINT)<pointername> + N) |
230 | alone | 159 | Или для PBYTE: |
8 | dimkam | 160 | &<pointername>[N] |
230 | alone | 161 | Для получения указателя на массив используется выражение: |
8 | dimkam | 162 | (PCHAR)<arrayname> |
230 | alone | 163 | При использовании вызова функции в качестве параметра надо ставить правильный тип указателя (указатель не равен массиву, в отличие от Си). |
8 | dimkam | 164 | |
230 | alone | 165 | Чтение по указателю допускается только с непосредственным приведением типа: *(<pointertype>)<pointervalue> |
8 | dimkam | 166 | |
230 | alone | 167 | Вызов функций оформляется как <procname>([<expression>,...]). |
168 | Процедуры и функции технически имеют плавающее число параметров, но не рекомендуется использовать это поведение. |
||
8 | dimkam | 169 | |
230 | alone | 170 | В выражениях <expression> имеются следующие приоритеты операций: |
171 | 1. Индексные квадратные скобки: arr[] или pointer[] |
||
172 | 2. Префиксы (+, -, ~ (инверсия), ! (логическое отрицание), & (взятие адреса переменной &<variable>, процедуры/функции &<func>, элемента массива &<array>[<expression>], поля структуры &(<structname>-><field>)), * (чтение по указателю)). |
||
173 | 3. Умножение, деление, &, &&. |
||
174 | 4. Сложение, вычитание, |, ^, ||, -> (адресация элемента структуры по указателю). |
||
637 | alone | 175 | 5. Сравнения и сдвиги (<<, >>). Сравнения и сдвиги работают только для типов BYTE, CHAR, INT, UINT. Сдвиги - также для LONG. Для BOOL разрешены только сравнения на равенство и неравенство. Сравнение на равенство можно писать == или =, но второй вариант несовместим с Си. |
8 | dimkam | 176 | |
230 | alone | 177 | В выражениях допустимы следующие виды значений: |
178 | * идентификатор переменной - получает тип по типу переменной. |
||
637 | alone | 179 | * целая числовая константа без знака - получает тип BYTE (если запись в стиле 0xff с не более чем 2 цифрами или в стиле 0b111 с не более чем 8 цифрами), LONG (если в конце стоит L) или UINT (в остальных случаях). |
180 | * целая числовая константа со знаком (+ или -) - получает тип INT. |
||
181 | * числовая константа с плавающей точкой - получает тип FLOAT (который пока не поддерживается). |
||
230 | alone | 182 | * символьная константа 'c' или '\<символ>' (допустимы только '\n', '\r', '\t', '\0', '\'', '\"', '\\'), где <символ> - один символ - получает тип char. |
637 | alone | 183 | * строковая константа "строка" - получает тип PCHAR. Допускается запись строковых констант в виде "str1""str2" (между ними допустим перевод строки) для наглядности и для избегания переполнения буфера строки (79 символов) в компиляторе. Строковые константы создаются автоматически с нулевым кодом в конце. |
184 | * константное выражение +(expr) - получает тип по левому контексту. |
||
8 | dimkam | 185 | |
230 | alone | 186 | Целые числовые константы могут быть десятичные (100), шестнадцатеричные (0x10), двоичные (0b11), восьмеричные (0o177 или как вариант 0177 с выдачей предупреждения из-за двусмысленности записи). |
8 | dimkam | 187 | |
230 | alone | 188 | Команды, которые определяются по формату: |
8 | dimkam | 189 | |
230 | alone | 190 | * <labelname><:> - определить метку для перехода. Метка должна быть уникальной внутри текущей области видимости. Подчёркивание добавлено, чтобы метку было лучше видно в тексте программы. |
191 | * <label>([<expression>,...]) - вызвать процедуру. Нельзя ставить пробел перед открывающей скобкой (ср. формат if). |
||
192 | * <var>=<expression> - вычислить и записать в переменную. Тип выражения должен соответствовать типу переменной. Получить в указатель адрес массива можно так: poi=(PBYTE)arr. |
||
193 | * <var><[><expression><]>=<expression> - вычислить и записать в ячейку массива. Таким же образом можно писать в массив, адрес которого передан как указатель. Будьте осторожны, границы массива не проверяются! |
||
194 | Доступ к ячейкам массива на чтение делается так же: <var><[><expression><]> (в том числе если адрес массива передан как указатель). |
||
195 | * <var>-><field>=<expression> - вычислить и записать в поле структуры (которая лежит по указателю <var>). Допускаются цепочки ->. |
||
8 | dimkam | 196 | |
230 | alone | 197 | Команды, которые начинаются с ключевого слова: |
8 | dimkam | 198 | |
230 | alone | 199 | * const - определить константу: const<type><variable>[=<constnum>], где <constnum>::=[-|+]<num>|'<char>'|"<string>"["<string>"...] или для массивов/структур: const<type><variable><[><expr><]>={<constnum>[,<constnum>...]} |
200 | Пропуск значения или списка значений нужен для использования внешних констант (не из текущего компилируемого модуля). Список значений для массива должен соответствовать числу значений в массиве (не проверяется). |
||
201 | Нельзя определять два раза одну и ту же константу со значением (TODO проверять в ассемблере). См. выше про необъявленные константы. |
||
202 | * extern - описать тип внешней переменной: extern<type><variable> - допускается только снаружи процедур и функций (не проверяется). |
||
203 | * var - определить переменную: var<type><variable>[=<expression>] - если с присваиванием, то допускается только внутри процедур или функций (не проверяется). Это именно присваивание, а не определение начального значения переменной. Нельзя присваивать массивы (не проверяется). В рекурсивных процедурах и функциях присваивание в var запрещено (не проверяется), а после блока всех var всё последующее тело функции должно быть в фигурных скобках {} (не проверяется), иначе переменные не будут правильно восстанавливаться при рекурсии. |
||
204 | * var с квадратными скобками - определить массив фиксированного размера: var<type><variable><[><expression><]> - ячейки массива нумеруются с 0, то есть в массиве a[10] не существует ячейки с индексом 10. Индекс должен иметь тип byte или uint. |
||
476 | alone | 205 | * enum - определить последовательный ряд констант: enum{<enumconstname>[=<number>],<enumconstname>[=<number>]...} - первая константа получит значение 0, вторая 1 и т.д., =<number> меняет этот счётчик. Эти константы невидимы как переменные, поэтому для использования их надо либо объявить через const, либо использовать +<enumconstname> (см. выше про необъявленные константы). Разрешена запятая после последнего элемента. |
637 | alone | 206 | * evar {UINT var1 = 1, INT var2 = 2, FLOAT var3} и т.п. создаёт переменные заданого типа с заданными адресами. |
230 | alone | 207 | * poke* - вычислить и записать в память с нужным типом: poke*<pointervalue>=<expression> - ключевое слово нужно, чтобы можно было пропускать ; в конце операторов. |
208 | [* module - определить модуль (область видимости): module<label><command> - команда <command> создаётся в области видимости внутри текущей области видимости (например, если была область видимости mainmodule, то внутри команды "module submodule{...}" будет область видимости mainmodule.submodule. Можно повторно определять одну и ту же область видимости, чтобы добавлять туда что-то новое.] |
||
209 | * proc - определить процедуру: proc<procname>[recursive][forward]([<type><par>,...])[<command>] - тоже создаёт область видимости внутри текущей области видимости. Поэтому <procname> должно быть уникальным внутри текущей области видимости. Если есть слово forward, то тело процедуры/функции не создаётся, и <command> не нужна (используется при использовании внешних процедур/функций или если их тело описано ниже вызова). Если есть слово recursive, то локальные переменные сохраняются при входе-выходе в процедуру/функцию, а при вызове сохраняются старые значения параметров. Внутри рекурсивной процедуры/функции нельзя объявлять массивы (не проверяется). |
||
210 | * func - определить функцию: func<type><funcname>[recursive][forward]([<type><par>,...])[<command>] - тоже создаёт область видимости внутри текущей области видимости. Поэтому <funccname> должно быть уникальным внутри текущей области видимости. См. выше про слова recursive и forward. |
||
211 | * if - альтернатива: if (<boolexpression>) <command>[else<command>]; - ';' против ошибки "if (expr); cmd" и против ошибки вложенных неполных альтернатив. Пробел после if обязателен (ср. формат вызова процедуры). При вложенных if достаточно одного ';'. |
||
8 | dimkam | 212 | *TODO ifnot |
213 | *TODO ifz |
||
214 | *TODO ifnz |
||
230 | alone | 215 | * while - цикл с предусловием: while (<boolexpression>) <command>[;] - ';' против ошибки "while(expr);cmd". |
8 | dimkam | 216 | *TODO whilenot |
217 | *TODO whilez |
||
218 | *TODO whilenz |
||
230 | alone | 219 | * repeat - цикл с постусловием: repeat<command>until (<boolexpression>) - если сделать скобки необязательными, то не получится определить "логическое выражение верхнего уровня", которое можно оставить во флаге. |
8 | dimkam | 220 | *TODO untilnot |
221 | *TODO untilz |
||
222 | *TODO untilnz |
||
230 | alone | 223 | * break - выйти из цикла while или repeat или из блока switch: параметров не имеет, просто break. |
224 | * return - вернуть значение из функции: return<expression> - должна быть последней командой в функции. Тип возвращаемого значения должен соответствовать типу функции. |
||
225 | * goto - перейти на метку: goto<labelname> - разрешается только внутри текущей процедуры или функции. |
||
226 | * call - вызвать процедуру по указателю: call (<expression>) - тип выражения - любой указатель или UINT (это не проверяется). |
||
227 | * asm - ассемблерная вставка: asm(" cmd1"" cmd2""label1"...) - каждая команда генерируется как отдельная строка. Нельзя писать команды без пробелов или табуляций вначале - токенизатор и ассемблер их не поймут. |
||
228 | * inc - увеличить значение переменной на единицу: inc <var> |
||
229 | * dec - уменьшить значение переменной на единицу: dec <var> |
||
230 | * struct - объявить структуру: struct<structname>{<type1><field1>[;]<type2><field2>[;]...} - допускается использование в структуре указателей на структуры, объявленные выше, и на саму себя (для совместимости с Си надо в объявлении структуры поля, являющиеся указателями на структуры, предварять словом struct). Потом можно объявить указатель на структуру: var struct <structname>* <structpointername> - и использовать поля структуры: <structpointername>-><field> |
||
231 | * switch - блок множественного ветвления по значению типа byte: |
||
8 | dimkam | 232 | switch (<byteexpr>){ |
233 | case <byteconst>: [<commands>] |
||
234 | case <byteconst>: [<commands>] |
||
235 | ... |
||
236 | default: [<commands>] |
||
237 | }; |
||
230 | alone | 238 | Разрешается только один switch в процедуре/функции. Все названия веток <byteconst> должны быть описаны в const или enum. Поля "case <byteconst>:" и "default:" (ветка по умолчанию) эквивалентны меткам (label), поэтому они могут стоять в произвольных местах, а автоматический выход из веток не предусмотрен. Для этого можно использовать goto или break (break здесь выходит из switch). |
239 | * #include "filename" - вложенно включить текст из другого файла. Используется для подключения внешних определений (дублирования на данный момент запрещены). |
||
240 | * #define <constname> <value> или #define <costname> (<type>)(<expr>) - определить константу совместимо с Си. В отличие от const, можно преопределять. Требуется для констант в заголовочных файлах и констант размера массивов, можно и для других случаев. |
||
241 | # #undef <constname> - удалить ранее определённую константу (или любой другой идентификатор, но это будет несовместимо с Си). |
||
476 | alone | 242 | * условная компиляция (разрешается вложенная): #ifdef <defined_const> ... [#else] ... #endif или #ifndef <defined_const> ... [#else] ... #endif. |
230 | alone | 243 | * export - ставится перед определением (процедуры/функции, переменной, константного массива/структуры), смещение адреса которого нужно экспортировать в файл filename.D_. |
244 | * typedef <type> <typename> - определить новый тип на основе старого. Для структурных типов надо писать слева слово struct для совместимости с Си. |
||
8 | dimkam | 245 | |
230 | alone | 246 | Все команды с # должны начинаться с начала строки и занимать всю строку. |
8 | dimkam | 247 | |
230 | alone | 248 | В условиях (if, while, until) имеется оптимизация сравнений за счёт проверки, что они осуществляются на 1-м уровне вложенности выражения (скобки не учитываются - они являются частью синтаксиса команды). Поэтому на этом же уровне нельзя писать другие операции (например, нельзя a==b==c, можно (a==b)==c). Это не проверяется? |
249 | В вычислениях (присваивание, var, return, параметры вызовов) сравнения можно писать только в скобках, чтобы они не оказались на 1-м уровне вложенности (например, нельзя a = b==c, можно a = (b==c)). |
||
8 | dimkam | 250 | |
230 | alone | 251 | Рекомендуемый стиль оформления условий: |
637 | alone | 252 | IF (cond1) do1(); |
253 | IF (cond2) { |
||
8 | dimkam | 254 | do2(); |
637 | alone | 255 | }ELSE IF (cond3) { |
8 | dimkam | 256 | if (cond4) {do3(); |
637 | alone | 257 | }ELSE do4(); |
8 | dimkam | 258 | }; |
637 | alone | 259 | IF (cond5) { |
8 | dimkam | 260 | do5(); |
637 | alone | 261 | }ELSE IF (cond6) { |
8 | dimkam | 262 | do6(); |
637 | alone | 263 | }ELSE { |
8 | dimkam | 264 | do7(); |
265 | }; |
||
266 | |||
230 | alone | 267 | Рекомендуется все ключевые слова языка писать большими буквами, а в текущей версии - типы и STRUCT обязательно большими буквами. |
268 | Чтобы скомпилировать такой же исходник с помощью компилятора Си, нужно использовать #include "nedodefs.h". |
||
8 | dimkam | 269 | |
230 | alone | 270 | Такой синтаксис - один из вариантов, получившихся из следующих требований: |
271 | - при анализе команд использовать только левый контекст; |
||
272 | - любая команда определяется без поиска по меткам (зарезервированных слов нет); |
||
273 | - при этом должна обеспечиваться совместимость с компилятором Си. |
||
8 | dimkam | 274 | |
230 | alone | 275 | Структура проекта |
8 | dimkam | 276 | |
230 | alone | 277 | Проект состоит из модулей на NedoLang (могут включать другие модули через #include) и на ассемблере (могут включать другие ассемблерные модули через include). Модули по одному передаются в компилятор (кроме включаемых через #include), потом все ассемблерные файлы надо токенизировать, потом надо вызвать NedoAsm с именем стартап-файла *.s. |
8 | dimkam | 278 | |
230 | alone | 279 | Стартап-файл - ассемблерный файл с запускающим кодом, должен включать (через include "filename.ast") остальные файлы проекта. В реальности вместо filename.ast будет грузиться filename.A_ и т.п. |
8 | dimkam | 280 | |
230 | alone | 281 | nedolang.exe - компилятор, имеет следующие параметры: |
282 | filename1.c filename2.c ... - компилировать перечисленные модули по одному. |
||
283 | -C - включить выдачу комментариев (для компиляции). |
||
284 | -H - включить выдачу подробного лога (для компиляции). |
||
285 | Выходные файлы компилятора: |
||
286 | *.ast - ассемблерный текст с кодом |
||
287 | *.var - ассемблерный текст с данными |
||
288 | err.f - ошибки компиляции |
||
289 | [label.f - метки во внутреннем формате] |
||
8 | dimkam | 290 | |
230 | alone | 291 | nedotok.exe - токенизатор, вызывается так: |
20 | dimkam | 292 | nedotok filename1.ast filename2.var filename3.s filename4.i ... |
230 | alone | 293 | (выходные файлы будут называться filename1.A_, filename2.V_ и т.п.). |
8 | dimkam | 294 | |
230 | alone | 295 | nedoasm.exe - ассемблер, вызывается так: |
8 | dimkam | 296 | nedoasm filename.A_ |
230 | alone | 297 | Выходные файлы ассемблера: |
298 | filename.bin - все сгенерированные байты кода и данных подряд |
||
299 | filename.org - данные релокации (в формате: 2 байта смещение, где лежит релоцируемый адрес) |
||
300 | [label0.f..label7.f - метки во внутреннем формате] |
||
301 | filename.pst - пост-метки во внутреннем формате (пока не поддерживается) |
||
302 | asmerr.f - ошибки ассемблирования |
||
303 | filename.D_ - токенизированный файл деклараций (команды export label в ассемблере) |
||
304 | Ассемблер выгружает код подряд, поэтому допустим только один ORG - начальный. Выравнивать данные можно через DS -$&mask. |
||
8 | dimkam | 305 | |
230 | alone | 306 | nedoexp.exe - детокенизатор (отладочный экспорт токенизированного ассемблера), вызывается так: |
8 | dimkam | 307 | nedoexp filename.A_ |
230 | alone | 308 | (выходной файл exp.f) |
8 | dimkam | 309 | |
230 | alone | 310 | Библиотеки: |
8 | dimkam | 311 | |
230 | alone | 312 | - Ассемблерный файл lib.i содержит стандартные математические функции (умножение, деление, сдвиги) для Z80. |
313 | - Заголовочный файл str.h содержит функции работы со строками. Для его использования подключайте в стартапе под Z80 str.i (или же вместо него str.c компилируйте в str.ast, str.var и подключайте их в стартапе). |
||
314 | - Заголовочный файл io.h одержит функции работы с файлами. Для его использования подключайте в стартапе под Z80 iofast.i под TR-DOS или io_os.i под NedoOS (или же вместо него io.c под TR-DOS без функций низкого уровня компилируйте в io.ast, io.var и подключайте их в стартапе). |
||
315 | - runtime.h, runtime.i - необязательная рантайм-библиотека для Z80 (прерывания, порты). |
||
316 | - sprite.h, sprite.i - спрайтовая библиотека для Z80. |
||
317 | - print.h, print.i (print_os.i для NedoOS) - библиотека вывода текста для Z80 (setxy, prchar, nprintf). |
||
8 | dimkam | 318 | |
230 | alone | 319 | Утилиты для Windows: |
8 | dimkam | 320 | |
230 | alone | 321 | - nedotrd - работа с образом диска в формате TRD. Можно перевести на NedoLang, если в файловой библиотеке поддержать ftell(pos)/fseek. |
322 | - nedores - конвертор графических ресурсов. Пока затруднительно перевести на NedoLang из-за размера массива. |
||
323 | - nedopad - обрезка или дополнение файла до заданного размера. Можно перевести на NedoLang. |
||
324 | - _gui - простейший интерфейс ввода и компиляции исходника на NedoLang (можно с автоматической перекомпиляцией в процессе ввода) с просмотром ошибок и результата в ассемблерном виде. |
||
8 | dimkam | 325 | |
230 | alone | 326 | Утилиты на NedoLang: |
327 | - nedodefb - перевод файла в ассемблерное шестнадцатеричное представление с обрезкой или дополнением (пока только для Z80). Можно перевести на NedoLang. Необязательно при использовании предыдущей утилиты. |
||
328 | - nedodiff - сравнение двух файлов (пока только для Z80, т.к. использует print.i). |
||
329 | - batch - интерпретатор командных файлов (пока только для Z80, лежит по адресу 64000). |
||
330 | - nedodel - удаление файла. |
||
331 | - movedisk - уплотнение образа диска после удаления файлов. |
||
637 | alone | 332 | - diff - сравнение двух файлов. |
8 | dimkam | 333 | |
230 | alone | 334 | При указании путей в #include, include и в командной строке надо использовать прямую косую черту '/'. |
335 | Результат компиляции кладётся в ту же директорию, где лежит исходный файл. |
||
8 | dimkam | 336 | |
230 | alone | 337 | Формат ассемблерного текста зависит от архитектуры целевого процессора. |
8 | dimkam | 338 | |
230 | alone | 339 | Поддерживаются директивы: |
340 | - db <byte>,<byte>... (dcb для ARM Thumb) - скомпилировать байты |
||
341 | - dw <word>,<word>... (dcd для ARM Thumb) - скомпилировать слова |
||
342 | - dl <long>,<long>... (dcq для ARM Thumb) - скомпилировать двойные слова |
||
343 | - ds <size>[,<byte>] (space для ARM Thumb) - скомпилировать пустой блок заданного размера, можно 0 |
||
344 | - org <addr> - указать адрес размещения программы, с которого она будет работать |
||
345 | - align <2^n> (для ARM Thumb) - пропустить байты для выравнивания адреса размещения на адрес, кратный параметру (того же можно добиться с помощью ds -$&<2^n-1> |
||
346 | - end (для ARM Thumb - ничего не делает) |
||
347 | - include "filename.ast" - подключить исходник с заданным именем. Фактически подключается токенизированный filename.A_. Разрешается вложенность |
||
348 | - incbin "filename" - подключить бинарный файл |
||
349 | - <label>=<expression> - присвоить метку (можно переприсвоить) |
||
350 | - <label> equ <expression> - присвоить метку (можно переприсвоить - todo запретить) |
||
351 | - export <label> - выгрузить значение метки в файл filename.D_ (токенизированный) в виде <label>=_+<labelvalue>. Этот файл можно включать в ассемблерный текст через include "filename.dcl". |
||
8 | dimkam | 352 | - todo disp |
353 | - todo ent |
||
354 | |||
230 | alone | 355 | Текущий адрес компиляции можно получить с помощью знака $. |
8 | dimkam | 356 | |
230 | alone | 357 | Определяемая метка должна стоять в начале строки. |
8 | dimkam | 358 | |
230 | alone | 359 | Разрешается писать несколько команд в строке через пробел (через двоеточие пока не работает). |
8 | dimkam | 360 | |
230 | alone | 361 | Ассемблер двухпроходный. Кодогенерация происходит на втором проходе. На момент генерации каждой строчки уже должны быть вычислены все используемые метки, иначе выводится ошибка. |
8 | dimkam | 362 | |
230 | alone | 363 | dw $,$ работает как dw $ dw $ |