#import "@docs/bmstu:1.0.0":*
#show: student_work.with(
  caf_name: "Компьютерные системы и сети",
  faculty_name: "Информатика и системы управления",
  work_type: "лабораторной работе",
  work_num: "5",
  discipline_name: "Машинно-зависимые языки и основы компиляции",
  theme: "Связь разноязыковых модулей",
  author: (group: "ИУ6-42Б", nwa: "<NAME>"),
  adviser: (nwa: "<NAME>"),
  city: "Москва",
  table_of_contents: true,
)

= Выполнение лабораторной работы

== Цель работы
Изучение конвенций о способах передачи управления и данных при вызове из программы, написанной на языке высокого уровня, подпрограмм, написанных на ассемблере.

== Задание
Дан текст не более 255 символов. Слова отделяются друг от друга пробелами. Удалить из слов гласные буквы.

== Выполнение

=== Алгоритм

Первый модуль будет выводить диалоговое окно, в котором будет простой интерфейс с большим полем ввода и кнопкой для преобразования строки.

Второй модуль получает указатель на строку и преобразует ее до определенного символа. Символом остановки я выбрал обратную кавычку \` (ASCII 96). 

Алгоритм удаления я выбрал такой: проходим 1 раз по всему массиву, причем заводим 2 счетчика: один для вскх букв (будет смещением на текущую букву). Один для согласных букв: если буква согласная прибавляем к счетчику еденицу и записываем букву на получившееся смещение. Таким образом мы получили продвинутый алгоритм удаления смещением, но при этом остается место, которое было использованно для хранения гласных букв. Поэтому для 3 модуля помимо указателя будем передавать еще целочисленное значение -- количество букв, необходимое для вывода.

В этом модуле будет реализована перезапись вводной строки получившейся.

#img(image("flow.svg", width: 30%), [Схема алгоритма])

=== Релизация 1-го и 3-го модуля на языке высокого уровня
Для реализации приложения на Qt необдодимо создать несколько файлов. Начнем с основного файла, в котором будет функция, вызывающая создание приложения -- `main.cpp`.

#code(read("qt/lab5/main.cpp"), "cpp", [Содержимое файла `main.cpp`])

Как видно в функции мы создаем приложение, после чего добавляем одно окно -- `MainWindow`. Напишем заголовочный и исполняемый файлы окна.

#code(read("qt/lab5/MainWindow.h"), "cpp", [Содержимое файла `MainWindow.h`])

#code(read("qt/lab5/MainWindow.cpp"), "cpp", [Содержимое файла `MainWindow.cpp`])

В заголовочном файле добавлено описание 3 наовых функций класса: ```cpp void on_pushButton_clicked()```, ```cpp void printstr(char* tx, int out)``` и ```cpp void parseasm(char* tx)```. ```cpp void on_pushButton_clicked()``` обрабатывает событие нажатия на конпку, т.е. готовит строку для вызова ассемблерной процедуры, поэтому эту процедуру можно назвать 1 модулем. ```cpp void printstr(char* tx, int out)``` получает строку на вход и количество букв, которые необходимо вывести, поэтому эта процедура будет 3 модулем. 

Теперь необходимо подготовить .pro файл для компиляции Qt. В этот файл пишут все связи между файлами.

#code(read("qt/lab5/lab5.pro"), "make", [Содержимое файла `lab5.pro`])

Необходимо пояснить, что сам ассемблер мы компилируем вручную -- с помощью командной строки и утилиты `nasm`. А программе даем просто соединить линковщиком уже скомпилированный код. Но чтобы использовать ассемблерную процедуру необходимо соблюсти сигнатуру -- назвать метку так же, как назвается процедура после помпиляции заготовочного файла. Обычно это название содержит в себе имя процедуры и некоторые ее параметры. 

#img(image("6.png", width: 100%), [Сигнатура `parseasm`])

Точно так же и в ассемблере, что бы использовать процедуру си необходимо найти название, присвоенное метке начала этой процедуры `gcc` и вызвать процедуру с помощью ```asm call```, при этом не стоит забывать подготовливать регистры.

#img(image("5.png", width: 100%), [Сигнатура `printstr`])

=== Реализация второго модуля на языке ассемблера
==== Начало
Для второго модуля напишем отдельную процедуру на ассемблере. Ее название обсуждалось ранее.

#let lab5 = parserasm(read("qt/lab5/lab5.asm"))
#code(funcstr(lab5, "_ZN10MainWindow8parseasmEPc:")+ funcstr(lab5, "txparser:"), "asm", [Начало 2 модуля])

В начале сгружаем используемые регистры. Си пытается передавать все аргументы в регистрах, поэтому вызовем отладку и посмотрим в каких регистрах нам передали ссылку на строку и `this`, так как эта процедура находится внутри класса.

#img(image("3.png", width: 100%), [Отладка Qt на уровне ассемблера])

Видно, что перед вызовом процедуры были помещены некоторые значения в регистры ```asm rsi``` и ```asm rdi```. Так как в c++ предполагается, что в первую очередь в процедуре будут использоваться значения передаваемые явно, то `this` всегда передается в последнюю очередь. Таким образом получаем, что в регистре ```asm rsi``` ссылка на первую букву текста, а в регистре ```asm rdi``` ссылка на ```asm this```.

Напишем цикл, который будет проходить по всем буквам массив
у и удалять гласные. Для 2 счетчиков, описанных ранее, будем использовать регистры ```asm rcx```(счетчик согласных) и ```asm rdx```(счетчик всех букв).

==== Цикл

#code(funcstr(lab5, "txparserl:"), "asm", [Начало цикла])

В начале цикла перемещаем следующую букву и проверяем условие выхода. Если условие не соблюдается начинаем работать методом исключения: перебираем все гласные, если есть совпадение переходим к следующей итерации цикла, если совпаданий нет прибавляем единицу в счетчик согласных и ставим букву на ее новое место.

#code(funcstr(lab5, "txparserln:"), "asm", [Обработка согласных])

После этого переходим на новую итерацию.

==== Конец

В конце, когда цикл завершит работу останется только вызвать 3 модуль для вывода результата. Для начала, чтобы точно убедтьься в какие регистры необходимо вводить значения проверим 3 модуль в отладчике.

#img(image("4.png", width: 100%), [Отладка Qt на уровне ассемблера])

Видно, что теперь изменяют значения 3 регистра: ```asm rsi``` --  ссылка на текст, ```asm edx``` -- количество букв, ```asm rdi``` -- `this`. ```asm rsi``` и ```asm rdi``` уже содержат значения, необходимые для передачи в функцию, необходимо положить в ```asm edx``` количество выводимых букв -- количество гласных и вызвать процедуру, после чего выйти из ассемблерной процедуры, не забыв вернуть остальные регистры в исходное состояние.

#code(funcstr(lab5, "txparsere:"), "asm", [Конец ассемблерной процедуры])

Это практически весь файл, осталось только добавить пару инструкция для ассемблера, чтобы он передал линкеровщику глобальные метку процедуры метода 2 и взял из скомпиллированного файла метрку процедуры метода 3.

#code("extern _ZN10MainWindow8printstrEPci
global _ZN10MainWindow8parseasmEPc", "asm", [Экспорт и импорт меток])

=== Тестирование

Тестровать будем на примере текста-рыбы Lorem. Просто вставим этот текст в окно.

#img(image("1.png", width: 70%), [Тестирование: вставляем текст])

После этого необходимо нажать на кнопку и текст в окне будет преобразован.

#img(image("2.png", width: 70%), [Тестирование: преобразованный текст])

== Код для формы

Тут представлен код для организации формы.

#let ui = read("qt/lab5/mainwindow.ui")
#raw(writeft(ui, 0, 43), lang:"xml")

#raw(writeft(ui, 44, 50), lang:"xml")

== Вывод

В процессе выполнения лабораторной работы были изучены конвенции о способах передачи управления и данных при вызове из программы, написанной на языке высокого уровня, подпрограмм, написанных на ассемблере.


== Контрольные вопросы

1. *Что такое «конвенции о связи»? В чем заключается конвенция register?* Конвенции о связи - ряд способов декларировать передачу параметров при организации связи разных модулей (вызывающей и вызываемой программы). При регистровой конвенции доступно 3 регистра при вызове подпрограммы: ```asm rax```, ```asm rdx```, ```asm rcx```, все остальные параметры хранятся в стеке.
2. *Что такое «пролог» и «эпилог»? Где располагается область локальных данных?* Пролог и эпилог - стандартное оформление входа и выхода программы соответственно. При вызове программы выполняется пролог, в ходе которого в стеке сохраняется значение ```asm rbp``` вызывающей программы, копии параметров и область локальных переменных подпрограммы.
3. *Как связана структура данных стека в момент передачи управления и текст программы и подпрограмм?* В момент получения управления подпрограммой в регистрах находятся до 6-ти параметров в виде значений или адресов (например, если передать указатель на начало строки), а в стеке – адрес возврата в вызывающую программу (адрес 64х разрядный для 64х разрядной системы).
4. *С какой целью применяют разноязыковые модули в одном проекте?* Разноязыковые модули позволяют дополнить функционал языка высокого уровня. Также возможна оптимизация высоконагруженных функций, которые возможно ускорить за счет замещения стандартныой функции или метода ассемблерной программой.Контрольные вопросы

