1. Знакомство с NEM Assembly
NEM Assembly (Native Execute Machine Assembly) — это низкоуровневый язык программирования для виртуальной машины NEM, разработанный как компонент игрового проекта "zabvensk". Язык предоставляет разработчикам полный контроль над виртуальным оборудованием: регистрами, оперативной памятью и периферийными устройствами ввода-вывода.
Архитектура NEM спроектирована с учётом образовательных целей и задач игровой логики, где требуется тонкое управление ресурсами. Синтаксис языка интуитивно понятен разработчикам, знакомым с ассемблером x86, но имеет ряд упрощений и специализированных инструкций для работы с векторами и математическими операциями.
1.1. История версий
| Версия | Дата | Основные изменения |
|---|---|---|
| 1.0 | 17.02.2026 | Первая стабильная версия. Включает базовый набор инструкций, поддержку векторов (vec2, vec3, vec4), математические операции (sin, cos, tan), операции с массивами и условные переходы. |
1.2. Назначение и область применения
NEM Assembly 1.0 является необязательной частью игрового проекта "zabvensk"
1.3. Основные возможности
- Регистровая архитектура. Восемь регистров общего назначения (r1-r5, rnd, rnr, rvc) для быстрых вычислений.
- Строгая типизация. Поддержка байтовых (byte), коротких (short), вещественных (float) и длинных (double) чисел, а также строк (string).
- Векторная математика. Встроенные типы vec2, vec3, vec4 с поддержкой всех арифметических операций и функций расстояния.
- Матрицы. Поддержка двумерных матриц строкового и вещественного типов с доступом по индексу Y*X.
- Управление памятью. Ручное выделение и освобождение памяти под переменные и массивы с контролем переполнения.
- Условные и безусловные переходы. Полноценная поддержка меток и стековых вызовов (call/ret).
- Ввод-вывод. Работа с консолью (out, inp) и отслеживание состояния клавиатуры (keyboard.key).
1.4. Структура программы
Программа на NEM Assembly состоит из последовательности инструкций и меток. Выполнение начинается с метки __start и завершается при достижении метки __stop или инструкции hlt. Блоки кода объявляются с помощью директивы .p.
; Пример минимальной программы
__start:
mov r1, 404
out r1, 1
hlt
__stop:
clear registres
1.5. Соглашения о синтаксисе
- Регистры обозначаются как r1, r2, r3, r4, r5, rnd, rnr, rvc.
- Метки начинаются с буквы и могут содержать буквы, цифры и символ подчёркивания. Завершаются двоеточием.
- Блоки объявляются директивой
.p имя_блока:. - Комментарии начинаются с точки с запятой (
;) и продолжаются до конца строки. - Строковые литералы заключаются в двойные кавычки (
"текст"). - Числовые литералы записываются в десятичном формате (отрицательные числа допускаются).
1.6. Специальные регистры
| Регистр | Назначение |
|---|---|
r1 - r5 |
Регистры общего назначения. Используются для хранения промежуточных результатов вычислений, адресов и счётчиков циклов. |
rnd |
Регистр случайного числа. Заполняется инструкцией rand и хранит сгенерированное значение. |
rnr |
Регистр остатка. Автоматически заполняется остатком от деления при выполнении инструкции div. |
rvc |
Регистр векторной дистанции. Содержит результат вычисления расстояния между векторами (инструкция dst). |
1.7. Среда выполнения
Интерпретатор NEM работает в составе виртуальной машины, обеспечивающей:
- Контроль использования оперативной памяти (максимум 262144 байта).
- Обработку системных прерываний (клавиатура, таймер).
- Защиту от переполнения стека и некорректных обращений к памяти.
При запуске программы через терминал NEM (команда run) производится полный синтаксический анализ, поиск меток и последующее выполнение кода строка за строкой. В случае ошибки выполнение прекращается с выводом кода ошибки.
Конец раздела 1.
1.8. Терминал NEM
Терминал NEM (Native Execute Machine Terminal) представляет собой интерфейс командной строки для управления виртуальной машиной, создания и выполнения программ на языке NEM Assembly. Терминал поддерживает два режима работы: главное меню и режим разработки NEM.
1.8.1. Запуск терминала
При запуске приложения пользователь попадает в главное меню системы. Приглашение командной строки имеет вид user:.
System Control num: 130022 user: _
1.8.2. Команды главного меню
Главное меню предоставляет базовые команды для управления системой и перехода в режим разработки.
| Команда | Описание | Пример |
|---|---|---|
help, ?, commands |
Вывод списка доступных команд. | user: help |
start nem |
Переход в режим разработки NEM. | user: start nem |
system monitor |
Отображение информации о состоянии виртуальной машины: использование RAM, частота процессора, значения регистров. | user: system monitor |
shutdown |
Завершение работы виртуальной машины и выход из приложения. | user: shutdown |
clear |
Очистка экрана консоли. | user: clear |
1.8.3. Системный монитор
Команда system monitor выводит подробную информацию о состоянии виртуальной машины:
- RAM — текущее использование оперативной памяти и процент заполнения.
- CPU — частота процессора (в текущей версии эмулируется).
- Регистры — текущие значения всех регистров (r1-r5, rnd, rnr, rvc) и их максимально допустимые значения.
------------------------------------------------------------ | RAM : 65836 / 262144 | 25% | CPU (1) 0.01 / 0.20 GHz | | Usage Registres CPU | r1: 404 / 9223372036854775807 | r2: 100 / 9223372036854775807 | r3: 0 / 9223372036854775807 | r4: 0 / 9223372036854775807 | r5: 0 / 9223372036854775807 | rnd: 0 / 9223372036854775807 | rnr: 0 / 9223372036854775807 | rvc: 0 / 9223372036854775807 ------------------------------------------------------------
1.8.4. Режим разработки NEM
После ввода команды start nem терминал переходит в режим разработки. Приглашение командной строки изменяется на nem:.
Native Execute Machine nem: _
1.8.5. Команды управления файлами в режиме NEM
| Команда | Описание | Пример |
|---|---|---|
new имя_файла |
Создание нового файла с программой. По умолчанию создаётся файл с приветственным кодом "welcome to hell". | nem: new program.n |
new имя_файла -e |
Создание пустого файла. | nem: new program.n -e |
new имя_файла -c |
Создание файла с шаблоном калькулятора. | nem: new calc.n -c |
delete имя_файла |
Удаление указанного файла. | nem: delete program.n |
list |
Вывод списка всех файлов в текущем каталоге. | nem: list |
1.8.6. Команды выполнения в режиме NEM
| Команда | Описание | Пример |
|---|---|---|
run имя_файла |
Запуск программы на выполнение. Производится синтаксический анализ, поиск меток и интерпретация кода. | nem: run program.n |
dis имя_файла |
Дизассемблирование файла — вывод hex-представления инструкций. | nem: dis program.n |
1.8.7. Дополнительные команды в режиме NEM
| Команда | Описание | Пример |
|---|---|---|
help, ?, commands |
Вывод списка команд режима NEM. | nem: help |
clear |
Очистка экрана консоли. | nem: clear |
exit |
Возврат в главное меню. | nem: exit |
1.8.8. Формат имени файла
В NEM Assembly рекомендуется использовать расширение .n или .nem для файлов с исходным кодом. При создании файла расширение должно быть указано явно. Все команды работы с файлами чувствительны к регистру символов.
; Примеры корректных имён файлов: program.n calc.nem test_001.n game_logic.n
1.8.9. Пример сессии работы в терминале
System Control num: 130022 user: start nem Native Execute Machine nem: new hello.n -e ; Создание пустого файла nem: list ---------------------- ./hello.n ---------------------- nem: run hello.n ; Запуск пустой программы (ничего не произойдёт) nem: exit ; Возврат в главное меню user: system monitor ; Просмотр состояния системы ------------------------------------------------------------ | RAM : 65536 / 262144 | 25% | CPU (1) 0.01 / 0.20 GHz ... ------------------------------------------------------------ user: shutdown ; Завершение работы
help.
Конец раздела 1.8. Следующий раздел: Типы данных в NEM.
2. Типы данных в NEM
В NEM Assembly реализована строгая система типов данных, которая определяет, как информация хранится в памяти и обрабатывается процессором. Каждая переменная или элемент массива принадлежит одному из предопределённых типов, что обеспечивает контроль над использованием памяти и корректность выполнения арифметических операций.
2.1. Классификация типов данных
Все типы данных в NEM делятся на три категории: скалярные (примитивные) типы, векторные типы и составные типы (массивы). Каждый тип имеет фиксированный размер в байтах, который учитывается при выделении памяти.
| Категория | Тип | Размер (байт) | Директива объявления | Диапазон значений |
|---|---|---|---|---|
| Скалярные | byte |
1 | db |
0 .. 255 (беззнаковый) |
short |
2 | dw |
-32768 .. 32767 | |
float |
4 | dd |
±1.5×10-45 .. ±3.4×1038 | |
double |
8 | dq |
±5.0×10-324 .. ±1.7×10308 | |
string |
переменный | ds |
Любая строка Unicode (длина ограничена RAM) | |
| Векторные | vec2 |
16 (2×8) | vec2 |
Две координаты типа double |
vec3 |
24 (3×8) | vec3 |
Три координаты типа double | |
vec4 |
32 (4×8) | vec4 |
Четыре координаты типа double | |
| Матрицы | string[][] |
строки × колонки × (переменный) | mx2_s имя, Y*X |
Двумерная матрица строк |
double[][] |
строки × колонки × 8 | mx2_q имя, Y*X |
Двумерная матрица чисел двойной точности | |
| Массивы | byte[] |
длина × 1 | arrb |
Массив байтов |
short[] |
длина × 2 | arrw |
Массив коротких целых | |
float[] |
длина × 4 | arrd |
Массив вещественных чисел | |
double[] |
длина × 8 | arrq |
Массив длинных вещественных | |
string[] |
длина × (переменный) | arrs |
Массив строк |
2.2. Скалярные типы данных
2.2.1. Байтовый тип (byte)
Тип byte предназначен для хранения беззнаковых целых чисел в диапазоне от 0 до 255. Занимает 1 байт памяти. Используется для экономии памяти при работе с небольшими числами, флагами состояния или индексами массивов. Объявляется директивой db.
; Примеры объявления байтовых переменных db counter, 10 ; counter = 10 db flag, 1 ; flag = 1 db zero, 0 ; zero = 0
2.2.2. Короткий целый тип (short)
Тип short хранит знаковые целые числа от -32768 до 32767. Размер — 2 байта. Оптимален для хранения координат, небольших счётчиков и идентификаторов объектов. Объявляется директивой dw.
; Примеры объявления коротких целых dw health, 100 ; health = 100 dw temperature, -15 ; temperature = -15 dw id, 4500 ; id = 4500
2.2.3. Вещественный тип (float)
Тип float (число с плавающей точкой одинарной точности) соответствует стандарту IEEE 754. Занимает 4 байта. Используется для хранения дробных чисел, когда достаточно точности примерно 6-7 значащих цифр. Объявляется директивой dd.
; Примеры объявления вещественных чисел dd speed, 12.5 ; speed = 12.5 dd gravity, -9.81 ; gravity = -9.81 dd coefficient, 0.33 ; coefficient = 0.33
2.2.4. Длинный вещественный тип (double)
Тип double (число с плавающей точкой двойной точности) обеспечивает высокую точность вычислений (примерно 15-16 значащих цифр). Занимает 8 байт. Рекомендуется для математических расчётов, где важна точность, например, при вычислении расстояний или тригонометрических функций. Объявляется директивой dq.
; Примеры объявления длинных вещественных dq pi, 3.14159265359 ; pi = 3.14159265359 dq distance, 12345.67 ; distance = 12345.67 dq result, 0.00000001 ; result = 1e-8
2.2.5. Строковый тип (string)
Тип string предназначен для хранения текстовых данных произвольной длины. Память выделяется динамически в соответствии с длиной строки. Строки могут содержать любые символы Unicode. Объявляются директивой ds.
; Примеры объявления строковых переменных ds name, "Player 1" ; name = "Player 1" ds message, "Hello, world!" ; message = "Hello, world!" ds empty, "" ; empty = ""
add) со строковыми переменными производится конкатенация строк. Арифметические операции (sub, mul, div) для строкового типа недопустимы и вызовут ошибку времени выполнения.
2.3. Векторные типы данных
Векторные типы являются составными структурами, объединяющими несколько координат типа double. Они оптимизированы для геометрических и физических расчётов, характерных для игровой логики. Все векторные операции выполняются покомпонентно.
2.3.1. Двумерный вектор (vec2)
Тип vec2 представляет точку или вектор в двумерном пространстве с координатами X и Y. Каждая координата хранится как double (8 байт), общий размер вектора — 16 байт. Объявляется директивой vec2.
; Пример объявления двумерного вектора vec2 player ; player.x = 0, player.y = 0 vec2 target
2.3.2. Трёхмерный вектор (vec3)
Тип vec3 представляет точку или вектор в трёхмерном пространстве с координатами X, Y, Z. Размер — 24 байта. Объявляется директивой vec3.
; Пример объявления трёхмерного вектора vec3 cube ; cube.x = 0, cube.y = 0, cube.z = 0 vec3 camera
2.3.3. Четырёхмерный вектор (vec4)
Тип vec4 представляет вектор в четырёхмерном пространстве с координатами X, Y, Z, W. Может использоваться для представления трёхмерных объектов с дополнительным параметром (например, весом или временем). Размер — 32 байта. Объявляется директивой vec4.
; Пример объявления четырёхмерного вектора vec4 space ; space.x = 0, space.y = 0, space.z = 0, space.w = 0
2.3.4. Доступ к компонентам векторов
Для обращения к отдельным координатам вектора используется точечная нотация: имя_вектора.координата. Координаты обозначаются строчными латинскими буквами: x, y, z, w.
; Примеры доступа к компонентам векторов mov player.x, 100 ; Установка координаты X add player.y, 5 ; Увеличение координаты Y на 5 out cube.z, 1 ; Вывод координаты Z
2.4. Матрицы
Матрицы в NEM представляют собой двумерные структуры данных, организованные по принципу "строка × столбец". Индексация начинается с 0. Доступ к элементу осуществляется по формуле Y * X, где Y — номер строки, X — номер столбца. Размер матрицы задаётся при объявлении и не может быть изменён во время выполнения.
2.4.1. Строковая матрица (mx2_s)
Тип mx2_s создаёт двумерную матрицу строк. Память выделяется под каждый элемент динамически в соответствии с длиной строки.
; Объявление строковой матрицы 10×10 mx2_s map, 10*10 ; Доступ к элементам матрицы out map[12*2], 1 ; Вывод элемента из строки 12, столбца 2 mov r1, map[8*8] ; Чтение элемента в регистр mov map[1*3], r1 ; Запись значения из регистра в матрицу ; Использование регистров для индексов mov r2, 5 mov r3, 7 mov map[r2*r3], "value" ; Запись строки в элемент [5,7]
2.4.2. Вещественная матрица (mx2_q)
Тип mx2_q создаёт двумерную матрицу чисел двойной точности (double). Каждый элемент занимает 8 байт.
; Объявление вещественной матрицы 5×5 mx2_q matrix, 5*5 ; Доступ к элементам матрицы mov matrix[2*3], 3.14159 ; Запись числа в элемент [2,3] out matrix[0*0], 1 ; Вывод элемента [0,0] ; Математические операции с элементами mov r1, matrix[1*1] add r1, 10 mov matrix[1*2], r1
2.4.3. Особенности работы с матрицами
- Индексация выполняется по формуле
[строка * столбец], где умножение интерпретируется как разделитель индексов. - В качестве индексов могут использоваться регистры, переменные и литералы.
- При выходе за пределы размерности матрицы возникает ошибка сегментации (код 0x06).
- Матрицы хранятся в памяти линейно, порядок элементов — строка за строкой.
map[12*2] обозначает обращение к элементу на пересечении 12-й строки и 2-го столбца. Операция умножения здесь не выполняется — это синтаксическая конструкция для указания двух индексов.
2.5. Массивы
Массивы в NEM позволяют хранить последовательности однотипных элементов. Индексация начинается с 0. Размер массива задаётся при объявлении и не может быть изменён во время выполнения программы. Доступ к элементам осуществляется по индексу в квадратных скобках.
2.5.1. Массивы байтов (arrb)
; Объявление массива байтов из 10 элементов arrb scores, 10 mov scores[0], 100 ; Присваивание значения первому элементу mov scores[9], 255 ; Присваивание значения последнему элементу
2.5.2. Массивы коротких целых (arrw)
; Объявление массива short из 20 элементов arrw temperatures, 20 mov temperatures[5], -10
2.5.3. Массивы вещественных чисел (arrd)
; Объявление массива float из 15 элементов arrd coefficients, 15 mov coefficients[0], 1.5
2.5.4. Массивы длинных вещественных чисел (arrq)
; Объявление массива double из 8 элементов arrq matrix, 8 mov matrix[3], 3.14159
2.5.5. Массивы строк (arrs)
; Объявление массива строк из 5 элементов arrs messages, 5 mov messages[0], "Error" mov messages[1], "Warning"
2.5.6. Использование регистров и выражений в индексах
В качестве индекса массива может использоваться не только числовой литерал, но и значение регистра, переменной или компонента вектора.
mov r1, 3 mov scores[r1], 75 ; scores[3] = 75 mov r2, scores[r1] ; r2 = scores[3] ; Использование координаты вектора как индекса vec2 pointer mov pointer.x, 5 mov scores[pointer.x], 100 ; scores[5] = 100
2.6. Преобразование типов
NEM Assembly выполняет неявное преобразование типов при выполнении арифметических операций. Преобразования осуществляются по следующим правилам:
- При операции с двумя разными числовыми типами результат приводится к более точному типу (byte → short → float → double).
- При присваивании значения переменной выполняется преобразование к типу целевой переменной с возможной потерей точности.
- При помещении числа в регистр оно всегда преобразуется в double (регистры хранят числа в формате double).
- Строковые типы не преобразуются в числовые автоматически (требуется явный ввод через inp).
2.7. Управление памятью
Каждое объявление переменной или массива увеличивает счётчик использованной оперативной памяти (RAM). Текущее состояние памяти можно контролировать через системный монитор. При превышении лимита в 262144 байта выполнение программы аварийно завершается.
| Тип данных | Потребление памяти |
|---|---|
| Регистры | Не потребляют RAM (хранятся в процессоре) |
| byte | 1 байт + имя переменной в таблице символов |
| short | 2 байта |
| float | 4 байта |
| double | 8 байт |
| string | длина строки в байтах |
| vec2 | 16 байт |
| vec3 | 24 байта |
| vec4 | 32 байта |
| mx2_s (Y×X) | сумма длин всех строк + (Y × X) байт служебных |
| mx2_q (Y×X) | Y × X × 8 байт |
| byte[n] | n байт |
| short[n] | n × 2 байт |
| float[n] | n × 4 байт |
| double[n] | n × 8 байт |
| string[n] | сумма длин всех строк + n байт (для нуль-терминаторов) |
Для освобождения памяти используется инструкция clear, которая удаляет переменную и возвращает занимаемую ей память в пул свободных ресурсов.
Конец раздела 2.
3. Векторы и структуры в NEM
Векторные типы данных представляют собой основу для выполнения геометрических и физических расчётов в NEM Assembly. В отличие от простых скалярных типов, векторы позволяют оперировать многомерными данными как единым целым, что существенно упрощает разработку игровой логики и математических алгоритмов.
3.1. Концепция векторных типов
Вектор в NEM — это структура, содержащая от двух до четырёх координат типа double. Все координаты хранятся с двойной точностью для обеспечения максимальной точности вычислений. Векторы могут представлять:
- Позиции объектов в двухмерном или трёхмерном пространстве.
- Направления и скорости движения.
- Силы и ускорения в физических расчётах.
- Цвета в формате RGBA (vec4 с компонентами red, green, blue, alpha).
- Кватернионы для представления вращений (vec4).
3.2. Объявление и инициализация векторов
Векторы объявляются с использованием директив vec2, vec3 или vec4. При создании все координаты автоматически инициализируются нулями. Для присваивания значений отдельным координатам используется инструкция mov с точечной нотацией.
; Объявление векторов vec2 position ; position.x = 0, position.y = 0 vec3 velocity ; velocity.x = 0, velocity.y = 0, velocity.z = 0 vec4 color ; color.x = 0, color.y = 0, color.z = 0, color.w = 0 ; Инициализация координат mov position.x, 10.5 mov position.y, -3.2 mov velocity.x, 5.0 mov velocity.y, 2.5 mov velocity.z, 0.0 mov color.x, 255 ; Красный компонент (R) mov color.y, 128 ; Зелёный компонент (G) mov color.z, 64 ; Синий компонент (B) mov color.w, 255 ; Прозрачность (A)
3.3. Арифметические операции с векторами
Над векторами могут выполняться все стандартные арифметические операции: сложение, вычитание, умножение и деление. Операции применяются покомпонентно ко всем координатам вектора.
3.3.1. Сложение и вычитание векторов
vec2 a, b mov a.x, 10 mov a.y, 20 mov b.x, 5 mov b.y, 7 add a, b ; a.x = 15, a.y = 27 sub a, b ; a.x = 10, a.y = 20 (после вычитания)
3.3.2. Умножение и деление вектора на скаляр
При умножении или делении вектора на число операция применяется к каждой координате.
vec3 scale mov scale.x, 2 mov scale.y, 4 mov scale.z, 6 mul scale, 2 ; scale.x = 4, scale.y = 8, scale.z = 12 div scale, 4 ; scale.x = 1, scale.y = 2, scale.z = 3
3.3.3. Операции с отдельными координатами
Для работы с конкретной координатой вектора используется точечная нотация. Это позволяет выполнять арифметические операции над отдельными компонентами.
vec2 point mov point.x, 100 mov point.y, 200 add point.x, 50 ; point.x = 150 sub point.y, 30 ; point.y = 170 mul point.x, 2 ; point.x = 300 div point.y, 10 ; point.y = 17
3.4. Специализированные векторные операции
NEM Assembly предоставляет набор встроенных инструкций для выполнения операций, специфичных для векторной математики.
3.4.1. Вычисление расстояния между векторами (dst)
Инструкция dst вычисляет евклидово расстояние между двумя векторами одинаковой размерности. Результат сохраняется в регистре rvc.
Формула вычисления расстояния для двумерных векторов:
distance = √[(x₂ - x₁)² + (y₂ - y₁)²]
Для трёхмерных векторов:
distance = √[(x₂ - x₁)² + (y₂ - y₁)² + (z₂ - z₁)²]
Для четырёхмерных векторов:
distance = √[(x₂ - x₁)² + (y₂ - y₁)² + (z₂ - z₁)² + (w₂ - w₁)²]
; Пример вычисления расстояния между двумя точками vec2 p1, p2 mov p1.x, 10 mov p1.y, 20 mov p2.x, 30 mov p2.y, 40 dst p1, p2 ; rvc = √[(30-10)² + (40-20)²] = √(400 + 400) = √800 ≈ 28.284 out rvc, 1 ; Вывод результата
3.4.2. Вычисление длины вектора (lng)
Инструкция lng вычисляет длину (модуль) вектора. Результат сохраняется в указанном регистре или переменной.
Формула длины для вектора любой размерности:
|v| = √(x² + y² + z² + ...)
; Пример вычисления длины вектора vec3 direction mov direction.x, 3 mov direction.y, 4 mov direction.z, 12 lng r1, direction ; r1 = √(9 + 16 + 144) = √169 = 13 out r1, 1
3.4.3. Вычисление квадрата длины вектора (lngsq)
Инструкция lngsq вычисляет квадрат длины вектора. Эта операция выполняется быстрее, чем вычисление длины, так как не требует извлечения квадратного корня. Часто используется для сравнения расстояний, когда точное значение не требуется.
; Пример вычисления квадрата длины vec2 point mov point.x, 5 mov point.y, 12 lngsq r1, point ; r1 = 5² + 12² = 25 + 144 = 169 out r1, 1
lngsq вместо lng для повышения производительности, так как извлечение квадратного корня — ресурсоёмкая операция.
3.4.4. Покомпонентные математические функции
Тригонометрические функции (sin, cos, tan) могут применяться как к скалярным значениям, так и к отдельным координатам векторов.
; Применение тригонометрических функций к координатам вектора vec2 rotation mov rotation.x, 45 ; Угол в градусах cos rotation.x, rotation.x ; rotation.x = cos(45°) ≈ 0.707 sin rotation.y, 45 ; rotation.y = sin(45°) ≈ 0.707
3.5. Использование векторов в качестве структур
Хотя векторы предназначены в первую очередь для математических операций, они могут использоваться как простые структуры данных для группировки связанных значений. Например, для хранения характеристик игрового объекта:
; Использование vec3 как структуры для хранения характеристик персонажа vec3 player_stats mov player_stats.x, 100 ; Здоровье (health) mov player_stats.y, 50 ; Энергия (energy) mov player_stats.z, 25 ; Боеприпасы (ammo) ; Использование vec4 для хранения цвета в формате RGBA vec4 pixel_color mov pixel_color.x, 255 ; Красный канал mov pixel_color.y, 128 ; Зелёный канал mov pixel_color.z, 64 ; Синий канал mov pixel_color.w, 255 ; Альфа-канал (прозрачность)
3.6. Передача векторов в подпрограммы
Векторы могут передаваться в блоки кода через регистры или глобальные переменные. Поскольку регистры хранят только скалярные значения, для передачи вектора целиком необходимо использовать его имя.
.p calculate_distance:
; Предполагается, что векторы p1 и p2 уже определены глобально
dst p1, p2
ret
.p main:
vec2 p1, p2
mov p1.x, 10
mov p1.y, 20
mov p2.x, 30
mov p2.y, 40
call calculate_distance
out rvc, 1
hlt
3.7. Особенности работы с векторами
3.7.1. Выравнивание в памяти
Все векторные типы выровнены по границе 8 байт, что обеспечивает эффективный доступ к координатам. Это важно учитывать при ручном управлении памятью и создании массивов векторов.
3.7.2. Совместимость типов
Векторы несовместимы со скалярными типами при прямых операциях. Нельзя сложить вектор с числом, используя операцию add без указания координаты. Для применения скалярной операции ко всем координатам необходимо использовать цикл или выполнять операцию над каждой координатой отдельно.
; Неправильно: add position, 5 ; Ошибка! Нельзя сложить вектор со скаляром ; Правильно: add position.x, 5 add position.y, 5 ; Для vec2 требуется две операции
3.7.3. Очистка векторов
Для освобождения памяти, занимаемой вектором, используется инструкция clear с именем вектора.
clear position ; Удаление вектора и освобождение 16 байт памяти
3.8. Примеры практического использования векторов
3.8.1. Перемещение объекта в двухмерном пространстве
; Объявление позиции и скорости объекта
vec2 position
vec2 velocity
; Инициализация
mov position.x, 0
mov position.y, 0
mov velocity.x, 2
mov velocity.y, 1
; Цикл перемещения (10 итераций)
mov r1, 10
.p loop:
add position, velocity
sub r1, 1
cmp r1, 0
ifh, go loop
; Результат: position.x = 20, position.y = 10
3.8.2. Проверка коллизии двух объектов
vec2 obj1, obj2 mov obj1.x, 15 mov obj1.y, 25 mov obj2.x, 18 mov obj2.y, 28 ; Вычисление расстояния между объектами dst obj1, obj2 ; Проверка, находятся ли объекты в радиусе 5 единиц cmp rvc, 5 ifl, go collision_detected
3.8.3. Нормализация вектора
vec3 direction mov direction.x, 3 mov direction.y, 4 mov direction.z, 0 ; Вычисление длины lng r1, direction ; r1 = 5 ; Нормализация (деление каждой координаты на длину) div direction.x, r1 div direction.y, r1 div direction.z, r1 ; direction теперь единичный вектор (0.6, 0.8, 0)
3.9. Ограничения и предостережения
- Нет проверки выхода за пределы. При работе с векторами не выполняется проверка на переполнение координат. Разработчик должен самостоятельно контролировать, чтобы значения не выходили за допустимые пределы типа double.
- Производительность. Операции с векторами выполняются интерпретатором последовательно, поэтому для критичных по времени участков кода рекомендуется минимизировать количество векторных операций.
- Отсутствие векторного произведения. В текущей версии NEM Assembly 1.0 не реализованы операции векторного и скалярного произведения векторов. При необходимости эти операции должны вычисляться вручную.
clear.
Конец раздела 3.
4. Системные инструкции NEM
Системные инструкции NEM Assembly обеспечивают взаимодействие программы с виртуальной машиной, управление памятью, вводом-выводом и выполнением кода. Данный раздел содержит полное описание всех системных инструкций, их синтаксиса и особенностей применения.
4.1. Классификация системных инструкций
Все системные инструкции NEM можно разделить на следующие категории:
- Инструкции перехода — управление потоком выполнения (go, call, ret, условные переходы).
- Инструкции сравнения — установка флагов состояния (cmp).
- Инструкции ввода-вывода — взаимодействие с пользователем (out, inp, keyboard.key).
- Инструкции управления памятью — создание и удаление переменных (db, dw, dd, dq, ds, arrb, arrw, arrd, arrq, arrs, mx2_s, mx2_q, clear).
- Инструкции стека — сохранение и восстановление состояния регистров (push, pop, pusha, popa).
- Инструкции синхронизации — задержки и ожидание (wait).
- Инструкции завершения — остановка выполнения (hlt).
4.2. Инструкции перехода
4.2.1. Безусловный переход (go)
Инструкция go выполняет безусловный переход на указанную метку. Выполнение программы продолжается с первой инструкции после метки.
| Синтаксис | Описание |
|---|---|
go метка |
Переход на указанную метку. |
__start:
go main ; Переход к блоку main
out "Это не будет выполнено", 1
.p main:
out "Привет, мир!", 1
hlt
4.2.2. Вызов подпрограммы (call)
Инструкция call выполняет переход на указанную метку с сохранением адреса возврата во внутреннем стеке. Возврат осуществляется инструкцией ret.
| Синтаксис | Описание |
|---|---|
call метка |
Вызов подпрограммы с сохранением адреса возврата. |
__start:
call subroutine ; Вызов подпрограммы
out "Возврат в main", 1
hlt
.p subroutine:
out "Выполняется подпрограмма", 1
ret ; Возврат в точку вызова
4.2.3. Возврат из подпрограммы (ret)
Инструкция ret выполняет возврат в точку вызова после инструкции call. Если стек вызовов пуст, выполнение переходит к метке __stop.
| Синтаксис | Описание |
|---|---|
ret |
Возврат из подпрограммы. |
4.2.4. Условные переходы
Условные переходы выполняются только при выполнении соответствующего условия, установленного предыдущей инструкцией cmp.
| Инструкция | Условие | Описание |
|---|---|---|
ife |
равно (equal) | Переход, если значения равны. |
ifn |
не равно (not equal) | Переход, если значения не равны. |
ifl |
меньше (low) | Переход, если первое значение меньше второго. |
ifh |
больше (high) | Переход, если первое значение больше второго. |
Синтаксис условных переходов: ifX go метка или ifX call метка.
; Пример использования условных переходов mov r1, 10 mov r2, 20 cmp r1, r2 ; Сравнение r1 и r2 ifl, go less ; Переход, если r1 < r2 ifh, go greater ; Переход, если r1 > r2 ife, go equal ; Переход, если r1 = r2
4.3. Инструкции сравнения
4.3.1. Сравнение значений (cmp)
Инструкция cmp сравнивает два значения и устанавливает флаги isEqual и isHigh, которые используются последующими условными переходами.
| Синтаксис | Описание |
|---|---|
cmp операнд1, операнд2 |
Сравнение двух операндов. |
В качестве операндов могут выступать:
- Регистры (r1-r5, rnd, rnr, rvc).
- Переменные любого типа (byte, short, float, double, string).
- Элементы массивов.
- Элементы матриц (
map[5*3]). - Числовые и строковые литералы.
; Примеры сравнения cmp r1, 100 ; Сравнение регистра с числом cmp player.x, target.x ; Сравнение координат векторов cmp scores[5], r2 ; Сравнение элемента массива с регистром cmp name, "admin" ; Сравнение строковой переменной с литералом cmp map[2*5], 0 ; Сравнение элемента матрицы с числом
4.4. Инструкции ввода-вывода
4.4.1. Вывод данных (out)
Инструкция out выполняет вывод значения в консоль. Второй аргумент определяет количество символов новой строки после вывода.
| Синтаксис | Описание |
|---|---|
out значение, количество_строк |
Вывод значения с указанным количеством переводов строки. |
Значение может быть:
- Строковым литералом (
"текст"). - Регистром.
- Переменной любого типа.
- Элементом массива.
- Элементом матрицы (
map[12*2]). - Компонентом вектора (
player.x). - Вектором целиком (вывод всех координат).
; Примеры вывода out "Hello, World!", 1 ; Вывод строки с переводом строки out r1, 0 ; Вывод значения регистра без перевода строки out player, 2 ; Вывод всех координат вектора с двумя переводами out scores[5], 1 ; Вывод элемента массива out map[12*2], 1 ; Вывод элемента матрицы out "Результат: ", 0 out rvc, 1 ; Составной вывод
4.4.2. Ввод данных (inp)
Инструкция inp считывает строку, введённую пользователем, и преобразует её к типу целевой переменной или регистра.
| Синтаксис | Описание |
|---|---|
inp переменная |
Ввод значения с клавиатуры. |
; Примеры ввода inp r1 ; Ввод числа в регистр r1 inp name ; Ввод строки в переменную name inp player.x ; Ввод числа в координату X вектора inp scores[0] ; Ввод числа в первый элемент массива inp map[3*4] ; Ввод числа в элемент матрицы
4.4.3. Системная переменная keyboard.key
Специальная системная переменная keyboard.key содержит код последней нажатой клавиши. Обновляется асинхронно в реальном времени.
; Пример отслеживания нажатия клавиши
.p loop:
out keyboard.key, 1 ; Вывод кода нажатой клавиши
wait 100 ; Задержка 100 мс
go loop
4.5. Инструкции управления памятью
4.5.1. Создание скалярных переменных
| Инструкция | Тип | Размер | Пример |
|---|---|---|---|
db |
byte | 1 байт | db counter, 100 |
dw |
short | 2 байта | dw health, 1000 |
dd |
float | 4 байта | dd speed, 12.5 |
dq |
double | 8 байт | dq pi, 3.14159 |
ds |
string | длина строки | ds name, "Player" |
4.5.2. Создание массивов
| Инструкция | Тип | Размер элемента | Пример |
|---|---|---|---|
arrb |
byte[] | 1 байт | arrb scores, 10 |
arrw |
short[] | 2 байта | arrw positions, 20 |
arrd |
float[] | 4 байта | arrd coefficients, 15 |
arrq |
double[] | 8 байт | arrq matrix, 8 |
arrs |
string[] | переменный | arrs messages, 5 |
4.5.3. Создание матриц
| Инструкция | Тип | Размер элемента | Пример |
|---|---|---|---|
mx2_s |
string[][] | переменный | mx2_s map, 10*10 |
mx2_q |
double[][] | 8 байт | mx2_q matrix, 5*5 |
4.5.4. Удаление переменных (clear)
Инструкция clear удаляет переменную, массив, матрицу или вектор, освобождая занимаемую память. Также может использоваться для очистки регистров, экрана или состояния клавиатуры.
| Синтаксис | Описание |
|---|---|
clear имя_переменной |
Удаление переменной, массива, матрицы или вектора. |
clear registres |
Очистка всех регистров (установка в 0). |
clear screen |
Очистка экрана консоли. |
clear keyboard.key |
Сброс состояния клавиатуры (очистка последнего нажатия). |
; Примеры использования clear
clear player ; Удаление вектора player
clear scores ; Удаление массива scores
clear map ; Удаление матрицы map
clear registres ; Обнуление всех регистров
clear screen ; Очистка консоли
clear keyboard.key ; Сброс состояния клавиатуры
; Использование в цикле обработки клавиш
.p input_loop:
cmp keyboard.key, 0
ifn, go key_pressed
wait 10
go input_loop
.p key_pressed:
out "Нажата клавиша: ", 0
out keyboard.key, 1
clear keyboard.key ; Сброс для следующего нажатия
go input_loop
keyboard.key после каждого нажатия, иначе программа будет бесконечно реагировать на одно и то же событие. Инструкция clear keyboard.key сбрасывает значение системной переменной в 0.
4.6. Инструкции стека
Стек регистров позволяет временно сохранять состояние регистров и восстанавливать его позднее. Это особенно полезно в подпрограммах, где требуется сохранить вызывающую среду.
| Инструкция | Описание |
|---|---|
push регистр |
Сохранить значение указанного регистра в стек. |
pop регистр |
Восстановить значение регистра из стека. |
pusha |
Сохранить все регистры в стек. |
popa |
Восстановить все регистры из стека. |
; Пример использования стека
.p subroutine:
pusha ; Сохранить все регистры
mov r1, 404 ; Изменение регистров в подпрограмме
mov r2, 100
; ... работа с регистрами ...
popa ; Восстановить исходные значения регистров
ret
4.7. Инструкции синхронизации
4.7.1. Задержка (wait)
Инструкция wait приостанавливает выполнение программы на указанное количество миллисекунд.
| Синтаксис | Описание |
|---|---|
wait миллисекунды |
Задержка выполнения. |
; Пример использования задержки out "Старт", 1 wait 1000 ; Задержка 1 секунда out "Прошла 1 секунда", 1
4.8. Инструкции завершения
4.8.1. Остановка выполнения (hlt)
Инструкция hlt (halt) немедленно прекращает выполнение программы и передаёт управление блоку __stop.
| Синтаксис | Описание |
|---|---|
hlt |
Остановка выполнения. |
; Пример использования hlt
__start:
mov r1, 100
cmp r1, 100
ife, error
out "Всё хорошо", 1
hlt
.p error:
out "Ошибка", 1
hlt
4.9. Специальные тестовые инструкции
Для отладки программ предусмотрены специальные тестовые инструкции, которые не рекомендуется использовать в рабочем коде.
| Инструкция | Описание |
|---|---|
tst_cmp_ |
Вывод состояния флагов сравнения (isHigh, isEqual). |
tst_vars_ |
Вывод списка всех активных переменных. |
4.10. Коды ошибок
При возникновении ошибок интерпретатор выводит сообщение с кодом ошибки и номером строки.
| Код | Описание |
|---|---|
0x00 |
Инструкция не найдена. |
0x01 |
Адрес не существует (обращение к несуществующей переменной). |
0x02 |
Неверное количество аргументов. |
0x03 |
Неверное имя блока. |
0x04 |
Неверные аргументы инструкции. |
0x05 |
Повторное определение символа. |
0x06 |
Ошибка сегментации (нарушение доступа к памяти). |
0x07 |
Ошибка типизации. |
0x08 |
Некорректное имя адреса. |
Конец раздела 4.
5. Арифметические и математические инструкции NEM
Арифметические и математические инструкции составляют основу вычислительной мощности NEM Assembly. Они позволяют выполнять операции с числами, векторами, массивами, матрицами и обеспечивают поддержку тригонометрических функций, статистических вычислений и генерации случайных чисел.
5.1. Классификация арифметических инструкций
Все математические инструкции NEM можно разделить на следующие категории:
- Базовые арифметические операции — сложение, вычитание, умножение, деление (add, sub, mul, div).
- Тригонометрические функции — синус, косинус, тангенс (sin, cos, tan).
- Статистические операции — поиск минимума и максимума в массивах (min, max).
- Генерация случайных чисел — rand.
- Операции с массивами — сортировка (srt).
- Строковые операции — вычисление длины строки (len).
5.2. Базовые арифметические операции
5.2.1. Сложение (add)
Инструкция add выполняет сложение первого операнда со вторым. Результат сохраняется в первом операнде.
| Синтаксис | Описание |
|---|---|
add операнд1, операнд2 |
операнд1 = операнд1 + операнд2 |
Операндами могут быть:
- Регистры (r1-r5, rnd, rnr, rvc).
- Переменные всех числовых типов (byte, short, float, double).
- Элементы массивов.
- Элементы матриц (
map[5*3]). - Компоненты векторов (player.x).
- Векторы целиком (покомпонентное сложение).
- Строковые переменные (конкатенация).
- Числовые и строковые литералы.
; Примеры сложения add r1, 5 ; r1 = r1 + 5 add r1, r2 ; r1 = r1 + r2 add health, 10 ; health = health + 10 add player.x, r3 ; player.x = player.x + r3 add position, velocity ; Покомпонентное сложение векторов add map[2*3], 5 ; Увеличение элемента матрицы на 5 add message, "!" ; Конкатенация строк: message = message + "!"
5.2.2. Вычитание (sub)
Инструкция sub выполняет вычитание второго операнда из первого. Результат сохраняется в первом операнде.
| Синтаксис | Описание |
|---|---|
sub операнд1, операнд2 |
операнд1 = операнд1 - операнд2 |
Вычитание поддерживается для всех числовых типов, векторов и элементов матриц. Для строковых типов операция недопустима.
; Примеры вычитания sub r1, 5 ; r1 = r1 - 5 sub r1, r2 ; r1 = r1 - r2 sub health, damage ; health = health - damage sub position, offset ; Покомпонентное вычитание векторов sub map[0*0], 10 ; Уменьшение элемента матрицы на 10
5.2.3. Умножение (mul)
Инструкция mul выполняет умножение первого операнда на второй. Результат сохраняется в первом операнде.
| Синтаксис | Описание |
|---|---|
mul операнд1, операнд2 |
операнд1 = операнд1 × операнд2 |
Умножение поддерживается для всех числовых типов, векторов (умножение вектора на скаляр) и элементов матриц. Для строковых типов операция недопустима.
; Примеры умножения mul r1, 5 ; r1 = r1 × 5 mul r1, r2 ; r1 = r1 × r2 mul speed, coefficient ; speed = speed × coefficient mul position, 2 ; Умножение всех координат вектора на 2 mul map[1*1], 3 ; Умножение элемента матрицы на 3
5.2.4. Деление (div)
Инструкция div выполняет деление первого операнда на второй. Результат сохраняется в первом операнде, а остаток от деления автоматически сохраняется в регистре rnr.
| Синтаксис | Описание |
|---|---|
div операнд1, операнд2 |
операнд1 = операнд1 / операнд2; rnr = операнд1 % операнд2 |
Деление поддерживается для всех числовых типов, векторов (деление вектора на скаляр) и элементов матриц. При делении целых чисел остаток сохраняется в rnr. Для вещественных чисел остаток всегда равен 0.
; Примеры деления div r1, 5 ; r1 = r1 / 5, rnr = r1 % 5 div r1, r2 ; r1 = r1 / r2, rnr = r1 % r2 div position, 2 ; Деление всех координат вектора на 2 div score, 3 ; Если score = 10, то score = 3, rnr = 1 div map[4*2], 2 ; Деление элемента матрицы на 2
5.3. Тригонометрические функции
5.3.1. Косинус (cos)
Инструкция cos вычисляет косинус угла, заданного в градусах. Результат сохраняется в первом операнде.
| Синтаксис | Описание |
|---|---|
cos операнд1, угол |
операнд1 = cos(угол) |
; Примеры вычисления косинуса cos r1, 60 ; r1 = cos(60°) = 0.5 cos r1, r2 ; r1 = cos(r2) cos player.x, 45 ; player.x = cos(45°) cos map[2*2], 30 ; cos(30°) в элемент матрицы
5.3.2. Синус (sin)
Инструкция sin вычисляет синус угла, заданного в градусах. Результат сохраняется в первом операнде.
| Синтаксис | Описание |
|---|---|
sin операнд1, угол |
операнд1 = sin(угол) |
; Примеры вычисления синуса sin r1, 30 ; r1 = sin(30°) = 0.5 sin r1, r2 ; r1 = sin(r2) sin player.y, 90 ; player.y = sin(90°) = 1 sin map[1*0], 45 ; sin(45°) в элемент матрицы
5.3.3. Тангенс (tan)
Инструкция tan вычисляет тангенс угла, заданного в градусах. Результат сохраняется в первом операнде.
| Синтаксис | Описание |
|---|---|
tan операнд1, угол |
операнд1 = tan(угол) |
; Примеры вычисления тангенса tan r1, 45 ; r1 = tan(45°) = 1 tan r1, r2 ; r1 = tan(r2) tan direction.x, 30 ; direction.x = tan(30°) tan map[0*1], 60 ; tan(60°) в элемент матрицы
5.4. Статистические операции с массивами
5.4.1. Поиск максимального элемента (max)
Инструкция max находит максимальный элемент в массиве и сохраняет его в первом операнде.
| Синтаксис | Описание |
|---|---|
max операнд1, массив |
операнд1 = максимальный элемент массива |
Поддерживаются все типы массивов: byte[], short[], float[], double[], string[]. Для строковых массивов используется лексикографическое сравнение.
; Примеры поиска максимума arrb scores, 5 mov scores[0], 10 mov scores[1], 25 mov scores[2], 15 mov scores[3], 30 mov scores[4], 20 max r1, scores ; r1 = 30 arrs names, 3 mov names[0], "Alice" mov names[1], "Bob" mov names[2], "Charlie" max r1, names ; r1 = длина строки "Charlie" (7)
5.4.2. Поиск минимального элемента (min)
Инструкция min находит минимальный элемент в массиве и сохраняет его в первом операнде.
| Синтаксис | Описание |
|---|---|
min операнд1, массив |
операнд1 = минимальный элемент массива |
; Примеры поиска минимума arrb scores, 5 mov scores[0], 10 mov scores[1], 25 mov scores[2], 15 mov scores[3], 30 mov scores[4], 20 min r1, scores ; r1 = 10 arrs names, 3 mov names[0], "Alice" mov names[1], "Bob" mov names[2], "Charlie" min r1, names ; r1 = длина строки "Bob" (3)
5.5. Генерация случайных чисел
5.5.1. Случайное число в диапазоне (rand)
Инструкция rand генерирует случайное целое число в заданном диапазоне и сохраняет его в регистре rnd.
| Синтаксис | Описание |
|---|---|
rand минимум, максимум |
rnd = случайное число от минимум до максимум |
Границы диапазона включаются в интервал генерации. Могут быть отрицательными.
; Примеры генерации случайных чисел rand 1, 100 ; rnd = случайное число от 1 до 100 rand -50, 50 ; rnd = случайное число от -50 до 50 ; Использование случайного числа rand 0, 10 mov player.x, rnd ; player.x = случайное число от 0 до 10 mov map[3*4], rnd ; Запись случайного числа в матрицу
5.6. Операции с массивами
5.6.1. Сортировка массива (srt)
Инструкция srt выполняет сортировку массива по возрастанию.
| Синтаксис | Описание |
|---|---|
srt массив |
Сортировка массива по возрастанию |
Поддерживаются все типы массивов. Для строковых массивов выполняется лексикографическая сортировка.
; Пример сортировки массива
arrb numbers, 5
mov numbers[0], 404
mov numbers[1], 15
mov numbers[2], 8
mov numbers[3], 23
mov numbers[4], 4
srt numbers ; numbers = [4, 8, 15, 23, 404]
; Вывод отсортированного массива
mov r1, 0
.p loop:
out numbers[r1], 1
add r1, 1
cmp r1, 5
ifl, go loop
5.7. Строковые операции
5.7.1. Вычисление длины строки (len)
Инструкция len вычисляет длину строки и сохраняет результат в первом операнде.
| Синтаксис | Описание |
|---|---|
len операнд1, строка |
операнд1 = длина строки |
В качестве строки может использоваться строковая переменная, строковый литерал, элемент строкового массива или элемент строковой матрицы.
; Примеры вычисления длины строки ds message, "Hello" len r1, message ; r1 = 5 len r2, "World" ; r2 = 5 arrs words, 2 mov words[0], "Apple" mov words[1], "Banana" len r3, words[1] ; r3 = 6 mx2_s map, 2*2 mov map[0*0], "Matrix" len r4, map[0*0] ; r4 = 6
5.8. Комбинированные операции с регистрами
5.8.1. Использование специальных регистров
При выполнении арифметических операций автоматически заполняются специальные регистры:
rnd— результат выполненияrand.rnr— остаток от деления при выполненииdiv.rvc— результат вычисления расстоянияdstили длины вектораlng.
; Пример использования всех специальных регистров rand 1, 100 ; rnd = случайное число mov r1, 10 div r1, 3 ; r1 = 3, rnr = 1 vec2 a, b mov a.x, 3 mov a.y, 4 lng rvc, a ; rvc = 5 out rnd, 0 out " ", 0 out rnr, 0 out " ", 0 out rvc, 1
5.9. Примеры комплексных математических вычислений
5.9.1. Вычисление площади круга
; Программа для вычисления площади круга
__start:
dq radius, 5.0
dq pi, 3.14159
dq area, 0.0
; area = pi * radius^2
mov r1, radius
mul r1, r1 ; r1 = radius^2
mul r1, pi ; r1 = pi * radius^2
mov area, r1
out "Площадь круга: ", 0
out area, 1
hlt
5.9.2. Решение квадратного уравнения
; Решение уравнения ax^2 + bx + c = 0
__start:
dq a, 1.0
dq b, -5.0
dq c, 6.0
dq d, 0.0 ; Дискриминант
dq x1, 0.0
dq x2, 0.0
; Вычисление дискриминанта: d = b^2 - 4ac
mov r1, b
mul r1, r1 ; r1 = b^2
mov r2, 4
mul r2, a
mul r2, c ; r2 = 4ac
sub r1, r2
mov d, r1
; Проверка дискриминанта
cmp d, 0
ifl, go no_roots
ife, go one_root
; Два корня
sqrt r1, d ; r1 = sqrt(d)
mov r2, b
sub r2, r1 ; r2 = -b + sqrt(d)
mov r3, 2
mul r3, a
div r2, r3
mov x1, r2
mov r2, b
add r2, r1 ; r2 = -b - sqrt(d) (с учётом знака)
mov r3, 2
mul r3, a
div r2, r3
mov x2, r2
out "x1 = ", 0
out x1, 1
out "x2 = ", 0
out x2, 1
hlt
.p one_root:
; Один корень
mov r1, b
mov r2, 2
mul r2, a
div r1, r2
out "x = ", 0
out r1, 1
hlt
.p no_roots:
out "Нет действительных корней", 1
hlt
sqrt, которая отсутствует в текущей версии NEM Assembly 1.0. Для извлечения квадратного корня необходимо использовать комбинацию lng с вектором или реализовать приближённое вычисление.
5.9.3. Статистическая обработка данных
; Вычисление среднего арифметического и разброса
__start:
arrd data, 10
mov data[0], 12.5
mov data[1], 14.2
mov data[2], 13.8
mov data[3], 15.1
mov data[4], 11.9
mov data[5], 16.3
mov data[6], 12.8
mov data[7], 14.5
mov data[8], 13.2
mov data[9], 15.7
; Вычисление суммы
mov r1, 0
mov r2, 0
.p loop:
add r1, data[r2]
add r2, 1
cmp r2, 10
ifl, loop
; Вычисление среднего
div r1, 10 ; r1 = среднее, rnr = остаток
out "Среднее: ", 0
out r1, 1
; Поиск минимального и максимального
min r2, data
max r3, data
out "Минимум: ", 0
out r2, 1
out "Максимум: ", 0
out r3, 1
hlt
5.10. Ограничения и особенности
- Точность вычислений. Все математические операции выполняются с двойной точностью (double), однако при сохранении результатов в переменные меньшего размера (byte, short, float) происходит потеря точности.
- Остаток от деления. Регистр
rnrперезаписывается при каждой операцииdiv. Если необходимо сохранить остаток, его следует скопировать в другую переменную. - Тригонометрические функции. Все углы задаются в градусах. Результаты тригонометрических функций находятся в диапазоне [-1, 1].
- Случайные числа. Генератор случайных чисел инициализируется при запуске виртуальной машины. Последовательность случайных чисел недетерминирована.
- Сортировка массивов. Инструкция
srtизменяет исходный массив. При необходимости сохранить оригинальные данные следует создать копию массива.
5.11. Сводная таблица арифметических инструкций
| Инструкция | Описание | Пример |
|---|---|---|
| add | Сложение | add r1, 5 |
| sub | Вычитание | sub r1, r2 |
| mul | Умножение | mul speed, 2.5 |
| div | Деление (с остатком в rnr) | div score, 3 |
| cos | Косинус угла (градусы) | cos r1, 60 |
| sin | Синус угла (градусы) | sin r1, 30 |
| tan | Тангенс угла (градусы) | tan r1, 45 |
| max | Максимальный элемент массива | max r1, scores |
| min | Минимальный элемент массива | min r1, scores |
| rand | Случайное число в диапазоне | rand 1, 100 |
| srt | Сортировка массива | srt numbers |
| len | Длина строки | len r1, message |
Конец раздела 5. Документация NEM Assembly 1.0 завершена.
6. Примеры программ на NEM Assembly
В данном разделе представлены рабочие примеры программ на языке NEM Assembly, демонстрирующие различные возможности языка: от простых арифметических операций до сложной игровой логики. Все примеры могут быть скопированы и выполнены в среде NEM.
6.1. Калькулятор
Программа реализует простейший консольный калькулятор с поддержкой четырёх базовых операций. Пользователь вводит оператор и два числа, программа выводит результат.
;==================================================
; Калькулятор для NEM Assembly
; Поддерживает операции: +, -, *, /
;==================================================
__start:
ds operator, "" ; Переменная для хранения оператора
go calc
.p calc:
; Запрос оператора
out "Введите оператор (+, -, *, /, q - выход): ", 0
inp operator
; Проверка выхода
cmp operator, "q"
ife, go halt
; Запрос первого числа
out "Введите первое число: ", 0
inp r1
; Запрос второго числа
out "Введите второе число: ", 0
inp r2
; Выбор операции
cmp operator, "+"
ife, go plus
cmp operator, "-"
ife, go minus
cmp operator, "*"
ife, go multiply
cmp operator, "/"
ife, go divide
; Если оператор не распознан
out "Неизвестный оператор!", 1
go calc
.p plus:
add r1, r2
go output
.p minus:
sub r1, r2
go output
.p multiply:
mul r1, r2
go output
.p divide:
div r1, r2 ; r1 = результат, rnr = остаток
go output
.p output:
out "Результат: ", 0
out r1, 1
; Если была операция деления, выводим остаток
cmp operator, "/"
ifn, go calc
out "Остаток: ", 0
out rnr, 1
go calc
.p halt:
out "Работа завершена", 1
hlt
__stop:
clear operator
clear registres
6.2. Бенчмарк производительности
Данная программа выполняет серию арифметических операций в цикле для оценки производительности виртуальной машины. Количество итераций задаётся регистром r2.
;==================================================
; Бенчмарк для NEM Assembly
; Выполняет 3 миллиона итераций с арифметикой
;==================================================
__start:
mov r1, 1000 ; Начальное значение
mov r2, 3000000 ; Счётчик итераций (3 миллиона)
.p bench_loop:
; Серия арифметических операций
div r1, 5
mul r1, 5
sub r1, 900
add r1, 900
mul r1, r1
mul r1, r1
; Декремент счётчика и проверка условия
sub r2, 1
cmp r2, 0
ifh, go bench_loop ; Продолжаем, если r2 > 0
__stop:
out "Бенчмарк завершён", 1
out "Результат: ", 0
out r1, 2
clear registres
6.3. Игровая логика: перемещение объекта
Пример демонстрирует использование векторов для моделирования движения объекта в двумерном пространстве. Объект движется с постоянной скоростью, а программа выводит его координаты на каждом шаге.
;==================================================
; Моделирование движения объекта в 2D
; Объект движется от точки (0,0) к точке (100, 50)
;==================================================
__start:
vec2 position ; Текущая позиция
vec2 target ; Целевая позиция
vec2 direction ; Направление движения
vec2 velocity ; Вектор скорости
; Инициализация
mov position.x, 0
mov position.y, 0
mov target.x, 100
mov target.y, 50
; Вычисление направления к цели
mov direction.x, target.x
mov direction.y, target.y
sub direction, position
; Нормализация направления и установка скорости
lng r1, direction ; r1 = длина вектора направления
; Создание единичного вектора направления
mov velocity.x, direction.x
mov velocity.y, direction.y
div velocity.x, r1
div velocity.y, r1
; Установка скорости (5 единиц за шаг)
mul velocity.x, 5
mul velocity.y, 5
; Основной цикл движения
mov r2, 20 ; Количество шагов
.p loop:
; Вывод текущей позиции
out "Позиция: X=", 0
out position.x, 0
out " Y=", 0
out position.y, 1
; Обновление позиции
add position, velocity
; Декремент счётчика
sub r2, 1
cmp r2, 0
ifh, go loop
; Вывод финальной позиции
out "Конечная позиция: X=", 0
out position.x, 0
out " Y=", 0
out position.y, 2
__stop:
clear position
clear target
clear direction
clear velocity
clear registres
7.4. Обработка массива данных
Программа демонстрирует работу с массивами: заполнение случайными числами, сортировку, поиск минимального и максимального элемента, вычисление среднего арифметического.
;==================================================
; Статистическая обработка массива
;==================================================
__start:
arrd data, 20 ; Массив из 20 элементов
mov r1, 0 ; Счётчик для цикла
; Заполнение массива случайными числами от 0 до 100
.p fill_loop:
rand 0, 100
mov data[r1], rnd
add r1, 1
cmp r1, 20
ifl, go fill_loop
; Вывод исходного массива
out "Исходный массив:", 1
mov r1, 0
.p print_loop:
out data[r1], 0
out " ", 0
add r1, 1
cmp r1, 20
ifl, go print_loop
out "", 2
; Сортировка массива
srt data
; Вывод отсортированного массива
out "Отсортированный массив:", 1
mov r1, 0
.p print_sorted:
out data[r1], 0
out " ", 0
add r1, 1
cmp r1, 20
ifl, go print_sorted
out "", 2
; Поиск минимума и максимума
min r1, data
max r2, data
out "Минимальный элемент: ", 0
out r1, 1
out "Максимальный элемент: ", 0
out r2, 1
; Вычисление среднего арифметического
mov r1, 0 ; Сумма
mov r2, 0 ; Счётчик
.p sum_loop:
add r1, data[r2]
add r2, 1
cmp r2, 20
ifl, go sum_loop
div r1, 20 ; r1 = среднее, rnr = остаток
out "Среднее арифметическое: ", 0
out r1, 1
out "Остаток от деления суммы: ", 0
out rnr, 2
__stop:
clear data
clear registres
6.5. Работа с матрицами
Пример демонстрирует создание и обработку двумерной матрицы. Программа создаёт игровое поле 10×10 и позволяет пользователю просматривать и изменять его элементы.
;==================================================
; Работа с матрицей: игровое поле
;==================================================
__start:
mx2_s map, 10*10 ; Создание строковой матрицы 10×10
mov r1, 0 ; Счётчик строк
; Инициализация матрицы символами '.'
.p init_rows:
mov r2, 0 ; Счётчик столбцов
.p init_cols:
mov map[r1*r2], "."
add r2, 1
cmp r2, 10
ifl, go init_cols
add r1, 1
cmp r1, 10
ifl, go init_rows
; Установка начальной позиции игрока
mov map[5*5], "P"
.p main_loop:
; Вывод поля
clear screen
out "Игровое поле:", 2
mov r1, 0
.p print_rows:
mov r2, 0
.p print_cols:
out map[r1*r2], 0
out " ", 0
add r2, 1
cmp r2, 10
ifl, go print_cols
out "", 1
add r1, 1
cmp r1, 10
ifl, go print_rows
; Обработка клавиш
out "Используйте WASD для перемещения, Q для выхода", 1
wait 100
cmp keyboard.key, 0
ife, go main_loop
; Очистка текущей позиции
mov map[5*5], "."
; Обработка нажатий
cmp keyboard.key, 87 ; W
ife, go move_up
cmp keyboard.key, 65 ; A
ife, go move_left
cmp keyboard.key, 83 ; S
ife, go move_down
cmp keyboard.key, 68 ; D
ife, go move_right
cmp keyboard.key, 81 ; Q
ife, go exit
clear keyboard.key
go main_loop
.p move_up:
sub r5, 1
go update_position
.p move_down:
add r5, 1
go update_position
.p move_left:
sub r4, 1
go update_position
.p move_right:
add r4, 1
go update_position
.p update_position:
mov map[r5*r4], "P"
clear keyboard.key
go main_loop
.p exit:
out "Выход из программы", 1
__stop:
clear map
clear registres
6.6. Проверка коллизий
Программа моделирует проверку столкновений между движущимся объектом и статическими препятствиями с использованием векторных расстояний.
;==================================================
; Проверка коллизий в двумерном пространстве
;==================================================
__start:
vec2 player ; Позиция игрока
arrb obstacles, 5 ; Массив препятствий (индексы)
; Инициализация препятствий (векторы)
vec2 obs0, obs1, obs2, obs3, obs4
; Координаты препятствий
mov obs0.x, 10
mov obs0.y, 10
mov obs1.x, 25
mov obs1.y, 30
mov obs2.x, 40
mov obs2.y, 15
mov obs3.x, 60
mov obs3.y, 45
mov obs4.x, 80
mov obs4.y, 20
; Начальная позиция игрока
mov player.x, 0
mov player.y, 0
; Параметры движения
mov r5, 10 ; Количество шагов
mov r4, 15 ; Радиус коллизии
out "=== СИМУЛЯЦИЯ КОЛЛИЗИЙ ===", 2
.p move_loop:
; Вывод текущей позиции
out "Игрок: X=", 0
out player.x, 0
out " Y=", 0
out player.y, 1
; Проверка коллизий со всеми препятствиями
mov r1, 0 ; Индекс препятствия
.p check_collisions:
; Загрузка координат препятствия
cmp r1, 0
ife, go load_obs0
cmp r1, 1
ife, go load_obs1
cmp r1, 2
ife, go load_obs2
cmp r1, 3
ife, go load_obs3
cmp r1, 4
ife, go load_obs4
go next_step
.p load_obs0:
dst player, obs0
go check_distance
.p load_obs1:
dst player, obs1
go check_distance
.p load_obs2:
dst player, obs2
go check_distance
.p load_obs3:
dst player, obs3
go check_distance
.p load_obs4:
dst player, obs4
go check_distance
.p check_distance:
cmp rvc, r4
ifl, go collision_detected
add r1, 1
cmp r1, 5
ifl, go check_collisions
go next_step
.p collision_detected:
out "⚠ КОЛЛИЗИЯ с препятствием ", 0
out r1, 1
go next_step
.p next_step:
; Перемещение игрока
add player.x, 8
add player.y, 5
sub r5, 1
cmp r5, 0
ifh, go move_loop
.p end:
out "", 1
out "Симуляция завершена", 2
__stop:
clear player
clear obs0
clear obs1
clear obs2
clear obs3
clear obs4
clear obstacles
clear registres
6.7. Использование стека регистров
Пример демонстрирует работу со стеком регистров для сохранения и восстановления состояния между подпрограммами.
;==================================================
; Сохранение контекста с использованием стека
;==================================================
__start:
go main
.p subroutine:
; Сохраняем все регистры
pusha
; Работаем с регистрами в подпрограмме
mov r1, 404
mov r2, 100
mov r3, 500
out "Внутри подпрограммы: r1=", 0
out r1, 0
out " r2=", 0
out r2, 0
out " r3=", 0
out r3, 1
; Восстанавливаем исходные значения
popa
ret
.p main:
; Устанавливаем значения регистров
mov r1, 10
mov r2, 20
mov r3, 30
out "До вызова: r1=", 0
out r1, 0
out " r2=", 0
out r2, 0
out " r3=", 0
out r3, 1
; Вызываем подпрограмму
call subroutine
out "После возврата: r1=", 0
out r1, 0
out " r2=", 0
out r2, 0
out " r3=", 0
out r3, 2
hlt
__stop:
clear registres
.n или .nem и выполнены командой run в терминале NEM. Для работы примеров, использующих случайные числа, результаты могут отличаться при каждом запуске.