NEM Assembly

Руководство по языку ассемблера виртуальной машины Native Execute Machine

Версия 1.0 — 17 февраля 2026 г.
Светлая

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"

Примечание. Язык предназначен исключительно для использования внутри виртуальной машины NEM и не может быть скомпилирован в машинный код реального процессора. Все программы выполняются в безопасной среде интерпретатора, что исключает возможность повреждения основной системы.

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).
Примечание. При преобразовании вещественного числа в целое (например, double в byte) дробная часть отбрасывается без округления. Контроль переполнения целочисленных типов возлагается на разработчика.

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 не реализованы операции векторного и скалярного произведения векторов. При необходимости эти операции должны вычисляться вручную.
Важно. При использовании векторов в циклах следите за потреблением памяти. Каждый новый вектор увеличивает счётчик RAM. Не забывайте очищать неиспользуемые векторы инструкцией 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]          ; Ввод числа в элемент матрицы
Важно. При вводе в числовые переменные строка автоматически преобразуется в число. Если преобразование невозможно, в переменную записывается 0.
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°) в элемент матрицы
Примечание. Все тригонометрические функции работают с углами в градусах. Результат вычисляется с двойной точностью (double).

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. Для работы примеров, использующих случайные числа, результаты могут отличаться при каждом запуске.