Makexm2c
учебник по языку ассе́мблера Makexm2c
Введение[править]
Учебник[править]
Данный учебник учит программированию на Makexm2c.
Makexm2c[править]
Makexm2c — это язык ассемблера, предназначенный для создания программ для виртуальной машины «iiixmish2».[1]
iiixmish2[править]
Iiixmish2 — это виртуальная машина со своими командами и др. Она имеет 10 миллионов ячеек памяти, 30 пользовательских регистров, 8 регистров для потоков, 2000 ячеек видеопамяти, более 30 команд и экран 640×480 (но может отображаться меньше).[2] Iiixmish2 имеет команду NOP
, но она не выполняется и ничего не делает.
Получение необходимого[править]
Скачайте свободную реализацию iiixmish2 здесь, а также средства для разработки программ на Makexm2c здесь (makexm2c-tools). Вы соглашаетесь использовать их, соблюдая Downadow License 7.
Распакуйте полученные архивы. Скомпилируйте программы, используя OpenJDK 17.
Вам потребуется среда для запуска Java-программ, вы можете установить пакет openjdk-17-jre с его зависимостями в Debian:
$ sudo apt install openjdk-17-jre
Выполните java --version
; если команда не будет найдена, то что-то пошло не так.
«Hello, world!»[править]
Теперь мы можем написать программу «Hello, world!», она будет выводить на экран виртуальной машины «Hello, world!» (перевод — «Всем привет!», а буквально — «Здравствуй, мир!»).
Создайте файл «hello.asm» и вставьте в него следующее:
vstr 0000, "Hello, world!"
updd
Это код на Makexm2c, переведите его в код, понятный виртуальной машине iiixmish2, используя ассемблер из makexm2c-tools:
$ java downadow.makexm2c_tools.main.Assembler hello.asm hello
Отлично! Запустите iiixmish2, передав полученный файл:
$ java downadow.iiixmish2.main.Iiixmish2 hello --no-communication
Параметр --no-communication отключает, возможно, губительную для SSD-дисков коммуникацию.
Команды[3][4][править]
Код нашей программы состоит из двух команд — vstr
и updd
.
vstr[править]
Команда vstr
нужна для вывода строки на экран. Конечно, это можно сделать и без неё, но с ней это делается проще и эффективнее: сокращается размер программы. Имеет два аргумента:
Синтаксис | Описание |
---|---|
ЦЦЦЦ (Ц — цифра) |
Адрес ячейки видеопамяти, с которого будет располагаться строка |
"СТРОКА" |
СТРОКА для вывода |
updd[править]
Команда для обновления («перерисовки») экрана.
Регистры[править]
Как уже было сказано ранее, iiixmish2 имеет 30 пользовательских регистров (п. реги́стры) и 8 для потоков.
2, 3 и 4 п. регистры предназначены для обновления экрана, п. регистр 1 хранит нажатую клавишу. Избегайте использования п. регистров 5, 7, 9 и 11, т. к. они могут использоваться некоторыми командами (например, vstr
и vrst
).[4]
Операции[3][4][править]
Присваивание[править]
Команда mov
сохраняет число в п. регистр, число не должно превышать один байт. Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
АДРЕС п. регистра, который должен принять ЧИСЛО |
ЧИСЛО |
ЧИСЛО для сохранения |
Пример использования:
mov UR10, 64
Также можно вставлять 'СИМВОЛ'
вместо числа, пример:
mov UR10, 'j'
то же, что и
mov UR10, 106
Сложение[править]
Команда add
складывает два п. регистра, сохраняет результат в п. регистр. Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра, содержащего первое слагаемое |
ЧИСЛО |
адрес п. регистра, содержащего второе слагаемое |
ЧИСЛО |
адрес п. регистра, в который следует поместить сумму |
Пример использования:
add UR0 UR6, UR9
Запятые между аргументами и «UR» к адресам, — необязательны.
Вычитание[править]
Команда sub
вычитает из п. регистра значение другого п. регистра, результат сохраняется в п. регистр.
Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра, содержащего уменьшаемое |
ЧИСЛО |
адрес п. регистра, содержащего вычитаемое |
ЧИСЛО |
адрес п. регистра, в который следует поместить разность |
Пример использования:
sub UR6 UR8, UR0
Загрузка[править]
Команда ld
загружает значение п. регистра в другой п. регистр.
Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра, который следует загрузить в ДРУГОЙ п. регистр |
ЧИСЛО |
адрес ДРУГОГО п. регистра |
Пример использования:
ld UR1, UR0
Умножение[править]
Команда mul
умножает п. регистр на другой п. регистр, результат сохраняется в п. регистр.
Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра, содержащего множимое |
ЧИСЛО |
адрес п. регистра, содержащего множитель |
ЧИСЛО |
адрес п. регистра, в который следует поместить произведение |
Пример использования:
mul UR6 UR8, UR0
Деление[править]
Команда div
делит п. регистр на другой п. регистр, результат сохраняется в п. регистр.
Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра, содержащего делимое |
ЧИСЛО |
адрес п. регистра, содержащего делитель |
ЧИСЛО |
адрес п. регистра, в который следует поместить частное |
Пример использования:
div UR6 UR8, UR0
Получение остатка от деления[править]
Команда mod
делит п. регистр на другой п. регистр, остаток сохраняется в п. регистр.
Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра, содержащего делимое |
ЧИСЛО |
адрес п. регистра, содержащего делитель |
ЧИСЛО |
адрес п. регистра, в который следует поместить остаток |
Пример использования:
mod UR0 UR29, UR8
Инкремент и декремент[править]
Команда inc
увеличивает п. регистр на единицу. Пример использования:
inc UR0
Команда dec
уменьшает п. регистр на единицу. Пример использования:
dec UR0
Изменение знака[править]
Команда tnp
изменяет знак п. регистра, пример использования:
tnp UR14
Возведение в степень[править]
Пример:
exp UR0 UR6, UR8
mov2[править]
Команда mov2
выполняет то же, что и mov
, но размер второго аргумента увеличен до шести байтов.
Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра |
ЦЦЦЦЦЦЦ |
число для сохранения |
Пример использования:
mov2 UR0, 0004985
mov3[править]
Команда mov3
«склеивает» п. регистры как строки, сохраняет результат в п. регистр.
Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра, в который следует сохранить результат |
ЧИСЛО |
адрес первого п. регистра для «склеивания» |
ЧИСЛО |
адрес второго п. регистра для «склеивания» |
ЧИСЛО |
адрес третьего п. регистра для «склеивания» |
ЧИСЛО |
адрес четвёртого п. регистра для «склеивания» |
ЧИСЛО |
адрес пятого п. регистра для «склеивания» |
ЧИСЛО |
адрес шестого п. регистра для «склеивания» |
Пример использования:
mov3 UR0, UR29 UR28 UR27 UR26 UR25 UR24
Генерация числа[править]
Команда sel
выбирает число от нуля до ОПРЕДЕЛЁННОГО числа минус один, результат сохраняет в п. регистр.
Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра, содержащего ОПРЕДЕЛЁННОЕ число |
ЧИСЛО |
адрес п. регистра, в который следует сохранить результат |
Пример использования:
sel UR0, UR12
Комментарии[править]
Коммента́рии — это тексты в коде программы, которые не влияют на её работу, но, как правило, помогают лучше понять её. В Makexm2c нет обязательного спецсимвола, который отличал бы комментарии от команд.[3]
Метки[3][править]
Ме́тки — в Makexm2c — это мнемоники, которые ставятся в определённые места в коде программы. Обращение к метке вставляет на месте обращения адрес, по которому располагается эта метка в двоичной форме программы. Пример:
метка:
nop
nop
mov2 UR0, <метка>
Для обращения, название метки, заключённое в <
и >
, вставляется в нужное место в программе.
Именованные константы[3][править]
Для создания именованных констант используется директива .def
, пример:
.def %CONST% "0"
создаст именованную константу «CONST» со значением «0».
Обращение к именованным константам отличается от обращения к меткам лишь тем, что название заключается в %
, а не <
и >
.
Ветвление[3][4][править]
Задание
Потренируйтесь с пройденными командами.
В iiixmish2 есть команды для создания условных переходов. Рассмотрим, как использовать их в Makexm2c.
Равно[править]
Для создания перехода вроде «если значение п. регистра А равно значению регистра Б, то выполнить переход на <значение п. регистра В>», воспользуйтесь следующей конструкцией:
if АДРЕС0 == АДРЕС1, АДРЕС2
Название | Описание |
---|---|
АДРЕС0 |
адрес первого п. регистра для сравнения |
АДРЕС1 |
адрес второго п. регистра для сравнения |
АДРЕС2 |
адрес п. регистра, на значение которого нужно перейти |
Ясно? Идём дальше.
Неравно[править]
if АДРЕС0 != АДРЕС1, АДРЕС2
Больше[править]
if АДРЕС0 > АДРЕС1, АДРЕС2
Меньше[править]
if АДРЕС0 < АДРЕС1, АДРЕС2
Равно ×2[править]
if АДРЕС0 == АДРЕС1 && АДРЕС2 == АДРЕС3, АДРЕС4
(если п. регистры АДРЕС0
и АДРЕС1
равны, п. регистры АДРЕС2
и АДРЕС3
равны, то выполнить переход на <значение п. регистра АДРЕС4
>)
Видеопамять[править]
Как уже говорилось во введении, iiixmish2 имеет 2000 ячеек видеопамяти.
Почти все ячейки видеопамяти могут отображаться на экране. Последние две ячейки, — 1998-ая и 1999-ая, — хранят параметр цвета для фона и параметр цвета для отображаемых ячеек видеопамяти. Возможные цвета[4]:
Значение | Цвет |
---|---|
1 | белый |
2 | зелёный |
3 | голубой или синий |
4 | тёмно-зелёный |
5 | серый |
6 | красный |
7 | жёлтый |
иное | чёрный |
Пример изменения цвета отображаемых ячеек видеопамяти на жёлтый:
mov UR0, 7
vsv UR0, 1999
Загрузка и сохранение[3][4][править]
Для загрузки значения из видеопамяти в п. регистр, используйте команду vld
. Её аргументы:
Синтаксис | Описание |
---|---|
ЦЦЦЦ |
адрес ячейки видеопамяти для загрузки |
ЧИСЛО |
адрес п. регистра-приёмника |
Пример использования:
vld 0001, UR8
Для сохранения значения п. регистра в ячейку видеопамяти, используйте vsv
. Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра для сохранения |
ЦЦЦЦ |
адрес нужной ячейки видеопамяти |
Пример использования:
vsv UR0, 0063
Для сохранения значения п. регистра в видеопамять как число, используйте vsvan
. Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра для сохранения |
ЦЦЦЦ |
адрес ячейки видеопамяти, с которой следует начать запись |
Пример использования:
vsvan UR0, 0000
Вы также можете вписывать адрес п. регистра вместо ЦЦЦЦ
в аргументах этих команд.
Вы можете печатать разными цветами! Для этого, измените ячейку видеопамяти, на месте которой будет изменение цвета последующих, на 255 плюс нужное значение цвета.[4]
Текст в самой нижней строке отображается полужирным.
Задание
Напишите программу, которая складывает два числа и выводит результат на экран.
Вывод линий[править]
Для вывода линии, дайте значение цвета (отличное от нуля) ячейке видеопамяти 1900 + НОМЕР_ЛИНИИ * 5 и дайте x1, y1, x2, y2 (например, 200, 100, 280, 101) следующим ячейкам после неё.
Память[править]
Программы iiixmish2 загружаются в его па́мять (насколько это возможно).[4]
Пользовательских регистров слишком мало, чтобы хранить в них хотя бы строку из 32-ух символов; поэтому вам следует хранить большинство значений в памяти.
Загрузка и сохранение[3][4][править]
Команда ild
предназначена для загрузки значений из памяти в п. регистры. Аргументы:
Синтаксис | Описание |
---|---|
ЦЦЦЦЦЦЦ |
адрес ячейки памяти для загрузки |
ЧИСЛО |
адрес п. регистра-приёмника. |
Пример использования:
ild 6001900, UR0
Для сохранения значения п. регистра в память, используйте isv
. Аргументы:
Синтаксис | Описание |
---|---|
ЧИСЛО |
адрес п. регистра для сохранения |
ЦЦЦЦЦЦЦ |
адрес нужной ячейки памяти |
Пример использования:
isv UR10, 0999999
Вы можете вписывать адрес п. регистра вместо ЦЦЦЦЦЦЦ
в аргументах этих команд.
Многопоточность[править]
В iiixmish2 встроены команды TH1
, TH2
и TH3
. Используя их, вы можете выполнять задачи одновременно. Эти команды имеют один аргумент — адрес п. регистра, со значения которого следует начать новую задачу.[4] Пример программы:
mov2 UR0, <thread>
th1 UR0
end
thread:
vstr 0000, "OK!"
updd
Недостаток vstr
[править]
Максимальное значение символа в vstr
-строке ограничено одним байтом[4], т. е. вы не можете печатать дополнительные символы на экран iiixmish2, используя эту команду (например, буквы русского алфавита).
Но есть Java-программа Print, которая поставляется с выпуском makexm2c-tools. Использование[5]:
$ java downadow.makexm2c_tools.main.Print [-r|--user-register АДРЕС] [-a|--address АДРЕС] "СТРОКА"
Эта программа выводит Makexm2c-код, который должен выводить любой доступный символ на экран iiixmish2; но код, который он создаёт, нехорош, т. к. программа не оптимизирует сохранение в видеопамять повторяющихся символов, из-за чего код получается большим.
Задание
Напишите программу, которая выводит «Здравствуй, мир!», используя Print из makexm2c-tools.
Простая программа[править]
Ниже представлен пример программы, в которой пользователь может вводить данные. То, что от символа «;» до конца строки будет комментарием.
mov UR29, 30
mov2 UR28, <main>
main:
; ждать 30 миллисекунд
lslp UR29
; если клавиша не была нажата, то перейти к main
if UR1 == UR0, UR28
; сохраняем клавишу (указателем текущей ячейки будет п. регистр 27)
vsv UR1, UR27
; увеличиваем указатель
inc UR27
; сбрасываем нажатую клавишу
mov UR1, 0
; обновляем экран
updd
; переход к main
jmp UR28
Задание
Измените программу так, чтобы фон стал белым, а текст, вводимый пользователем, — чёрным. Помните, как собирать и запускать программы?
.ascii и .goto[править]
.ascii <mem0> "hello world"
приказывает ассемблеру записать строку «hello world», начиная с яч. <mem0>. .goto +100
приказывает перейти ассемблеру на 100 яч. больше, это похоже на 100 nop'ов.
Общение с другими компьютерами[править]
Iiixmish2 и другие компьютеры (включая ЭВМ, на котором выполняется iiixmish2) могут «общаться» через файлы коммуникации. Данные, которые передал компьютер, сохраняются в памяти iiixmish2, начиная с адреса 9999872. Чтобы ответить, запишите краткий ответ, начиная с адреса 9999000.
Ещё команды[3][править]
Это последний раздел изучения. В нём описываются команды slp
, off
, vrst
и end
.
slp[править]
slp
ждёт <значение п. регистра А> секунд (для миллисекунд есть lslp
). Пример использования:
slp UR0
off[править]
off
завершает работу iiixmish2.
vrst[править]
vrst
заменяет значения почти всех ячеек видеопамяти на неотображаемое (0), предназначение — убрать текст с экрана. Не путайте его с vstr
.
end[править]
Если цикл выполнения памяти встретит команду end
, то он должен будет завершиться.
Задание
Напишите программу, которая должна завершиться тогда, когда пользователь нажмёт какую-нибудь клавишу.