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

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

Изучение команд обработки цепочек и приемов обработки символьной информации

== Задание

Дан текст, состоящий из слов, разделенных несколькими пробелами. Определить количество букв Р в каждом слове.

== Выполнение
=== Алгоритм

Так как словами являемся любой набор символов, разделенный пробелами создадим цикл, в котором будет 4 проверки.

1. Проверка на пробел (Если 1-ый -- выводим результат для текущего слова)
2. Проверка на Р (Если истина -- прибавляем к счетчику 1)
3. Проверка на выход (Если истина -- выводим последнее слово и выходим)
4. Проверка на первую букву (Если истина -- запоминаем смещение (для вывода) и обнуляем счетчик Р)

Вот схема алгоритма:

#img(image("flow.svg", width: 64%), [Схема алгоритма для основной программы])

=== Выделение памяти

Выделим 255 байт для хранения введенного текста и 4 байта для хранения ответа. Проинициализируем несколько строк для красивого вывода.

#let dz1 = parserasm(read("dz1.asm"))
#code(funcstr(dz1, "section .data") + funcstr(dz1, "section .bss"), "asm", [Объявление и инициализация данных])


=== Начало программы

В начале выведем приглашения для ввода текста пользователю и считаем текст. 

#code(funcstr(dz1, "_start:"), "asm", [Выводим приглашение на ввод и ожидаем ввода])

После начнем готовить регистры к циклу. ```asm ecx``` будет отвечать за общий счетчик, объявлен он 1 из-за особенностей обработки первой буквы слова, о которой я расскажу позже.```asm esi``` будет флагом общего выхода -- для того, чтобы не дублировать код для вывода последнего слова, я просто вызываю метку вывода, а в этой метке проверяю ```asm esi```: если 0 -- возвращаемся к циклу, если 1 -- выходим из программы.

#code(funcstr(dz1, "txparser:"), "asm", [Готовим несколько регистров перед циклом])

=== Цикл
==== Начало цикла

Цикл я разделил на сного частей, в начале цикла обрабатываются начала слов: сюда мы входим в 1 раз из ```asm txparser``` и каждый раз, когда предыдущим символом был пробел, а текущий пробелом не является.

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

Так как начальную букву необходимо обработать, а в общем теле цикла ```asm ecx``` всегда увеличивается на 1 будем проходить по этой букве 2 раза: в первый будем загружать адрес этой буквы в ```asm edx```, ```asm eax``` будет счетчиком букв, во второй будем обрабатывать в цикле эту букву, как и все буквы. Условием входа в это начало будет 0 в регистре ```asm edx```, который мы будем ставить после первого пробела, для того, чтобы отметить, что предыдущее слово закончено и выведено. Вторым условием обязательно болжно быть то, что текущяя буква не пробел.
#pagebreak()
==== Основное тело цикла

Тут мы будем обрабатывать все 4 условия из алгоритма.

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

В начале переносим текущую букву в регистр ```asm bl```, после чего увеличиваем счетчик букв на 1. 

Проверяем условие выхода: если текущая буква -- служебный знак переноса строки переходим к метке ```asm txparsere```.

Если текущая буква -- пробел переходим к обработке пробелов.

Если ```asm edx``` равен нулю, переходим к началу цикла -- у нас новая буква (она не пробел -- иначе мы бы даже не дошли до этого сравнения).

Если нам встретилась буква Р, неважно в каком регистре переходим к обработке букв Р. 

Если ни одно условие не выполнилось так же повторяем цикл.

==== Обработка пробелов

#code(funcstr(dz1, "txparserlsp:"), "asm", [Обработка пробелов])

Если ```asm edx``` не равен нулю, значит мы наткнулись на 1 пробел: толлько в таком случае мы будем выводить предыдущее слово. Поэтому последнее слово, не имеющее пробелов после необходимо будет выводить вручную. 

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

Добавляя к ```asm ecx``` общее смещение в памяти получим адрес конца слова. Вычитая адрес начала слова ```asm edx``` получим количество букв в этом слове. Таким боразом в ```asm ecx``` получили количество букв, а в ```asm edx``` адрес первой буквы, загрузим сначала адрес, потом количество в стек.

Необходимо подготовить и вывести первую часть предложения, которая будет выглядеть, как ```c "There are "``` и количество букв, которое мы сохранили ранее. Все это сделает моя библеотека, подробно описанная в лабораторной работе 2. Поэтому просто передаем память с цифрой и память с буквами первой части предложения в регистры, после чего вызываем ```asm outi```.

Далее выводим вторую часть предложения ```c " letter(s) 'p' in word "```. 

После чего выводим само слово, сначала помещаем в ```asm edx``` количество его букв, затем в ```asm ecx``` адрес, все это в таком порядке лежало в стеке.

Но этого не достаточно, после самого слова я хочу выводить знак переноса строки, поэтому помещаем в уже ненужный буфер для хранения количества букв код переноса строки 10, а в регистр количества букв перемещаем 1, таким образом и выводим только перенос строки.

После чего возвращаем все регистры обратно, кроме ```asm edx```, в него помещаем 0, как знак того, что следующие пробелы будут не первые и выводить слово не нужно.

Далее проверяем ```asm esi```, если он не равен нулю, то значит текущее слово последнее и мы можем выходить из программы.


#code(funcstr(dz1, "txparserlpr:"), "asm", [Вывод слова и количества букв])

==== Обработка буквы Р

Для обработки буквы, необходимо изменить значение счетчика на 1 и перейти к следующей итерации цикла.

#code(funcstr(dz1, "txparserlcmpiax:"), "asm", [Обработка буквы Р])

=== Выход и вывод последнего слова

При выходе еще раз вызовем метку ```asm parsetxlpr```, с ```asm esi``` равным 1.

#code(funcstr(dz1, "txparsere:"), "asm", [Вывод последнего слова])

После чего из метки перейдем к финальному выходу из программы.

#code(funcstr(dz1, "txparseree:"), "asm", [Финальный выход из программы])

== Тестировние программы

Тестировать программу будем на тексте-рыбе:

``` Loppem  pipsum  pdolor  sit  amet,      officia     excepteur    ex    fugiat    reprehenderit  enim        labore          culpppa      ppp     sint        ad       nisi    Lorem      papriatur      mollit      ex     esse     exercitation    amet.```

#img(image("test.png", width: 55%), [Тестирование программы])

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

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

1. *Дайте определение символьной строки.*

Символьная строка -- массив символов, оканчивающийся спецсимволом (обычно концом строки(10) или 0)

2. *Назовите основные команды обработки цепочек?*

- пересылка цепочки: ```asm movs Адрес_приемника,Адрес_источника```
- сравнение цепочек: ```asm cmps Адрес_приемника,Адрес_источника```
- сканирование цепочки: ```asm scas Адрес_приемника```
- загрузка элемента из цепочки: ```asm lods Адрес_источника```
- сохранение элемента в цепочке: ```asm stos Адрес_приемника```

3. *Какие операции выполняют строковые команды ```asm MOVS```? Какие особенности характерны для этих команд?*

```asm movs``` обрабатывает текущий элемент и изменяет текущий адрес, указывая на следующий элемент цепочки.

4. *Какие операции выполняют строковые команды ```asm CMPS, SCAS```? Какие особенности характерны для этих команд?*

```asm cmps``` сравнивает два цепочки и возвращает флаги так же как и ```asm cmp```. Используется совместно с операциями ```asm rep*``` и помогает организовать поиск по цепочке, сравнение цепочек и тд.

5. *Как обеспечить циклическую обработку строк?*

Необходимо использовать команду-префикс ```asm rep*```, в совокупности с условием команда помогает избежать организации цикла для операций с цепочками.

6. *Какова роль флага ```asm DF``` во флажковом регистре при выполнении команд обработки строк?*

Так как цепочечные команды сами организуют изменения регистров, флаг ```asm df``` погомает понять, в какую строну мы будем идти в памяти. Если флаг равен 0, то с каждой итерацией адрес увеличивается, иначе уменьшается.

7. *Как правильно выбрать тестовые данные для проверки алгоритма обработки строки?*

Необходимо взять максимально длинный текст, с большим количеством различных символов. Например, при проверке в своем варианте я использовал текст-рыбу с увеличенным количеством пробелов и букв "p".



