Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Процедуры формирования исполняемой программыСодержание книги Поиск на нашем сайте
Последовательность процедур формирования программы на языке ассемблера и совокупность порождаемых файлов показана на рисунке 2.
Рис. 2 – Процесс обработки ассемблер-программы
В процессе формирования программы на языке ассемблера выделено 4 этапа: - ввод исходного кода программы текстовым редактором, - трансляция программы, - создание загрузочного модуля, - отладка программы. Начальной процедурой создания программы на языке Ассемблера является ввод исходного текста программы в файл с расширением.asm. При этом может быть использован любой текстовый редактор, сохраняющий текст в виде стандартных кодов ASCII, например, редактор NC или блокнот или редактор, встроенный в интегрированную среду Ассемблера. Следующим шагом формирования программы является компиляция, которая носит специфическое название ассемблирование. Этот этап может быть выполнен программами ASM, MASM или TASM (сложность программ-компиляторов растет в указанной последовательности). Результатом выполнения этого этапа является программа в машинных кодах с расширением.obj, или, иначе, объектная программа, уже “понятная” микропроцессору. Естественно, перевод состоится лишь в том случае, если исходный текст программы не содержит ошибок. Одновременно с объектным файлом могут быть созданы файлы листинга (*.lst) и перекрестных ссылок (*.crf). Рекомендуется файл листинга создавать обязательно, поскольку при наличии ошибок в листинге описывается характер ошибки сразу после ошибочной команды, что значительно упрощает внесение исправлений, особенно на этапе обучения. Файл листинга содержит код ассемблера исходной программы, машинный (объектный) код каждой команды и ее смещение в кодовом сегменте (значение регистра IP). Кроме того, сообщения о найденных синтаксических ошибках в программе помещаются непосредственно после ошибочной команды (бывают исключения, когда ошибка не в самой команде, а ранее нее, но эти ситуации встречаются редко). Однако объектная программа еще не является законченной и исполняемой, т.к. в ней определены не все адреса (программа не является “перемещаемой”) и не объединены части (блоки) программы, которые могут транслироваться отдельно с целью более простой отладки. Преобразование объектной программы в исполняемую (компоновка) выполняется загрузчиком (редактором связей) LINK либо TLINK (в зависимости от используемой программы ассемблирования: для ASM, MASM – LINK, для TASM – TLINK). Чтобы проверить работоспособность созданной программы и увидеть результаты ее работы (если не использован вывод на дисплей), применяют программу отладчик. Тестирование и отладка исполняемой программы выполняется отладчиком TD или DEBUG.
МЕТОДИКА ВЫПОЛНЕНИЯ РАБОТЫ
Этапы создания исходного текста программы, трансляции и компоновки не требуют дополнительных разъяснений, поскольку при их выполнении необходимо ввести только имя программы. Отладчик td.exe, разработанный фирмой Borland International представляет собой оконную среду отладки программ на уровне исходного текста на языках Pascal, C, ассемблер. Основные возможности отладчика, наиболее широко используемые студентами - это: - выполнение трассировки программы в прямом направлении, при котором за 1 шаг выполняется одна машинная инструкция; - просмотр и изменения состояния аппаратных ресурсов микропроцессора во время командного выполнения программы. Управлять работой отладчика можно с помощью системы меню двух типов: - глобального, находящегося в верхней части экрана и постоянно доступного. Вызов меню осуществляется выбором курсором мыши или нажатием клавиши F10; - локального, учитывающего особенности окон и становящегося активным щелчком правой мыши или нажатием клавиш Alt+F10. Специфика программы на ассемблере в том, что делать выводы о правильности ее функционирования можно, отслеживая работу на уровне микропроцессора, обращая внимание на то, как изменяется состояние ресурсов микропроцессора и компьютера в целом. Общее поведение программы позволяет просмотреть режим безусловного выполнения, который вызывается нажатием клавиши F9. Однако для детального изучения работы программы рекомендуется применять режим выполнения программы по шагам, для вызова которых выбираются пункты меню Run -> Trace into ( прерывание или внутренняя процедура будут выполняться по шагам) или Run -> Step over (вызов процедуры или прерывание отрабатываются как одна обычная команда). В окне отладчика отражаются: - команды выполняемой программы, причем курсор устанавливается на текущей команде; - значения в регистрах и состояния флагов. Осуществляемые программой преобразования можно отслеживать в окне дампа оперативной памяти Dump, отражающего содержимое области памяти по адресу, указанному в левой части окна. Рекомендуемый порядок работы с отладчиком: а) вызвать на выполнение td.exe.; б) выбрать файл исполняемой программы, набрав комбинации клавиш FILE ->OPEN и имя Вашей программы в окне запроса. После ответа OK на сообщение об отсутствии символьной таблицы в окно CPU загружается программа с нулевого адреса относительно начала сегментного регистра кодов (для приведенного в конце описания лабораторной работы примера это будет команда PUSH DS); в) выбрать режим пошагового выполнения Run -> Step over. В окне CPU появляется окрашенный треугольник между относительным адресом команды и машинным кодом команды. Он показывает очередную команду, которая будет выполнена процессором после нажатия функциональной клавиши F8. Изменения, которые происходят в сегментных регистрах после выполнения команды, отмечаются белым цветом соответствующей строки в окне регистров. Пошаговый процесс выполнять до тех пор, пока не появится сообщение об окончании программы (с ключевым словом terminated); г) после выполнения команд, связанных с изменением содержимого ячеек памяти, нужно просматривать эти изменения командой VIEW -> DUMP. При отсутствии мыши скрыть окно дампа памяти можно нажатием функциональной клавиши F6.
ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ 1. С использованием редактора текста набрать следующую программу: TITLE EX_PROG PAGE,132 STACK SEGMENT PARA STACK ‘STACK’ DB 64 DUP(‘STACK ’); Область стека STACK ENDS DSEG SEGMENT PARA PUBLIC ‘DATA’ SOURCE DB 10,20,30,40; эта таблица будет скопирована DEST DB 4 DUP (?); в эту таблицу в обратном порядке DSEG ENDS SUBTTL ОСНОВНАЯ ПРОГРАММА PAGE CSEG SEGMENT PARA PUBLIC ‘CODE’ ASSUME CS:CSEG, DS:DSEG, SS:STACK OUR_PROG PROC FAR ;занести в стек такие начальные значения, чтобы программа ; могла возвратить управление отладчику PUSH DS; поместить в стек номер блока адреса возврата SUB AX,AX; обнулить регистр АХ PUSH AX; поместить в стек значение адреса возврата MOV AX,DSEG; инициировать адрес сегмента данных MOV DS,AX ; скопировать таблицу SOURCE в таблицу DEST в обратном порядке, в качестве промежуточной ячейки пересылки использовать регистр AL MOV AL, SOURCE MOV DEST+3,AL MOV AL, SOURCE+1 MOV DEST+2,AL MOV AL, SOURCE+2 MOV DEST+1,AL MOV AL, SOURCE+3 MOV DEST,AL RET; возврат управления отладчику db OUR_PROG ENDP CSEG ENDS END OUR_PROG 2. Оттранслировать программу в объектный код. 3. Скомпоновать программу (получить исполнимый файл). Изучить листинг программы. 4. Провести отладку программы и проверить получаемые результаты. 5. Внести в программу следующие изменения: задать исходную таблицу SOURCE из 5 двухбайтовых шестнадцатеричных переменных и скопировать эту новую таблицу в DEST. 6. В программе пункта 5 в сегменте данных дополнительно определить переменные, заполнив их следующими значениями: - 5 байтовых переменных AA, BB, CC, DD, EE; - 5 двухбайтовых слов AABB, BBCC, CCDD, DDEE, EEFF; 7. Получить исполнимый файл программы с данными пункта 6и изучить дамп памяти данных с целью уяснения механизма размещения данных в оперативной памяти.
Требования к содержанию и оформлению отчета
Отчет должен включать: а) титульный лист; б) формулировку задачи работы; в) описание результатов выполнения пунктов 3-7: • листинги программ; • результаты выполнения программ; г) вывод, согласованный с задачей работы.
Критерии результативности выполнения работы
Лабораторная работа считается успешно выполненной, если: · отчет на бумажном носителе содержит все необходимые пункты согласно требованиям; · студент дает аргументированные пояснения по всем приведенным в отчете пунктам задания; · студент отвечает на контрольные вопросы преподавателя.
Контрольные вопросы 1. Какие группы регистров выделяются в микропроцессоре и каковы особенности их использования? 2. Какую функцию в микропроцессоре выполняет регистр флагов? 3. Как используется регистр команд IP? 4. Какие шаги необходимо выполнить для получения из программы на языке ассемблера исполняемого модуля? 5. Прокомментируйте содержание листинга программы.
ЛАБОРАТОРНАЯ РАБОТА № 2 РЕЖИМЫ АДРЕСАЦИИ ЦЕЛЬ И ЗАДАЧА РАБОТЫ
Целью работы является освоение режимов адресации структурных данных и закрепление навыков трансляции, компоновки и отладки программ. Задача работы – разработка простой линейной программы.
2. ОСНОВНЫЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ
Смещение, которое вычисляется операционным блоком для доступа к находящемуся в памяти операнду, называется исполнительным адресом операнда. Этот исполнительный адрес показывает, на каком расстоянии (в байтах) от начала сегмента располагается искомый операнд. В зависимости от используемого режима адресации получение исполнительного адреса может заключаться только в извлечении его как составной части исполняемой команды, а могут потребоваться дополнительные операции сложения составной части команды с содержимым других регистров. Различают адресацию операндов: - непосредственную, которая заключается в указании в команде самого значения операнда, а не его адреса; - прямую, предполагающую указание в команде непосредственно исполнительного адреса; - косвенную, при которой в команде указывается адрес регистра или ячейки памяти, в которых хранится адрес операнда или его составляющие; - неявную, когда адреса операндов в команде не указываются, а подразумеваются кодом операции. Непосредственная адресация имеет место, если операнд-источник является константой или переменной, которой присвоено постоянное значение. Например: MOV AX, 500 загружается значение 500 в РОН AX. Следует отметить, что непосредственный операнд может быть задан простым выражением, в котором константы или идентификаторы констант связаны арифметическими операциями +, -, * или / (в таких выражениях не должно быть скобок). Например: MOV AX, 156*10Н/2. Следует помнить, что диапазон посылаемых чисел (значений непосредственного операнда) определяется вместимостью приемника - если это однобайтовый регистр (AH, AL, BL …), то в него можно посылать беззнаковые числа в диапазоне от 0 до 255, знаковые – от –128 до 127. Регистровая адресация имеет место в командах, оперирующих с содержимым РОН или сегментных регистров в качестве одного или обеих операндов команды. Например, команда: MOV DS, AX копирует содержимое РОН АХ в сегментный регистр DS, при этом содержимое регистра AXне изменяется. Обратите внимание! При использовании этого вида адресации в программах необходимо следить, чтобы разрядности обеих регистров были одинаковы. Прямая адресация ячеек ОП имеет несколько вариантов: - прямая обычная характеризуется тем, что смещение является составной частью команды и не требует при формировании исполнительного адреса дополнительных регистров, иными словами. Обычно применяется, если операндом служит помеченная переменная, например: MOV AX, SOURCE загружает слово из ячейки памяти в регистр. При этом в памяти старший байт следует за младшим, а не предшествует ему. Это обусловлено тем, что в памяти ЭВМ старшая часть располагается в ячейках памяти со старшими адресами. Поэтому схема приведенной команды будет следующей: Таблица 1
После выполнения вышеуказанной команды пересылки регистр АХ будет содержать АХ=ААВВ; - прямая с индексированием использует арифметические операции для вычисления адреса. Покажем ее использование на примере следующих векторов (одномерных массивов): BYTE_TBL DB 12,15,-54,-19,20 WORD_TBL DW -456, 50, 21, 654,-176,5,41. Положим, требуется получить в РОН пятый байт из BYTE_TBL и второе слово из WORD_TBL. Такой результат можно получить выполнением команд: MOV DL, BYTE_TBL[4] MOV AX, WORD _TBL[4] По первой из приведенных команд в DL попадет значение 20, по второй в AX будет перенесено значение 50. Следует запомнить, что счет позиции элемента начинается с 1, а смещение считается с нуля, т.к. оно определяется относительно начала вектора! Аналогичный результат получим, если выполним следующую последовательность команд: MOV SI, 4 MOV DL, BYTE_TBL[SI] MOV AX, WORD _TBL[SI]. Последний вариант записи команд является более универсальным, поскольку позволяет, например, осуществлять последовательную обработку элементов вектора путем наращивания значений в индексном регистре SI. Такая адресация операндов из оперативной памяти называется индексной, поскольку часть адреса операнда (смещение) находится в индексном регистре; - прямая с базированием отличается от индексной тем, что смещение операнда относительно начала структуры данных находится в базовом регистреВХ: MOV BX, 4 MOV DL, BYTE_TBL[ВХ]; - прямая с индексированием и базированием использует базовый и индексный регистры для указания смещения, которое получается как сумма значений в обеих регистрах: MOV SI, 4 MOV BX, 4 MOV AX, WORD _TBL [ВХ][SI]. Последняя команда будет пересылать в регистр АX значение из ячейки памяти с адресом WORD _TBL+4+4= WORD _TBL+8 (это пятое слово со значением –176). Возможна и такая форма записи команды: MOV AX, WORD _TBL [ВХ+SI]. Косвенная или непрямая адресация означает, что в команде указан регистр, в котором находится адрес операнда. Иными словами, подлежащее обработке значение можно получить после двух обращений: сначала из регистра извлекается адрес ячейки оперативной памяти, а затем по этому адресу извлекается сам операнд. Для такой адресации используются регистры BX, BP, SI, DI, указанные в квадратных скобках, что означает ”в качестве адреса брать содержимое того адреса, на который указывает заключенный в квадратные скобки регистр”. Существует 2 варианта косвенной адресации ячеек ОП: - косвенная обычная, когда исполнительных адрес находится в регистре, например: MOV BX, offset BYTE_TBL MOV AH, [BX]. По этим командам в регистр AH будет занесено значение 12 (элемент вектора BYTE_TBL со смещением 0 относительно начала вектора). Чтобы адрес-смещение переменной мог оказаться в регистре, используется команда пересылки вида MOV <регистр> offset <имя переменной> (между знаками <> должны быть вставлены конкретные значения). Т.к. содержимое регистра легко изменить в ходе выполнения программы, данный способ адресации позволяет динамически назначить адрес операнда для некоторой машинной команды. Это свойство применяется для организации циклических вычислений и для работы со структурами данных типа таблиц и массивов; - косвенная с индексированием: от предыдущей отличается тем, что исполнительный адрес берется в виде суммы адресов, находящихся в базовом и индексном регистрах: MOV AН, [BX+SI]. Адресация с базированием и индексированием очень полезна при работе с двумерными массивами и таблицами. В ней исполнительный адрес вычисляется как сумма значений базового регистра, индексного регистра и (возможно) сдвига, который указывается как непосредственное значение. В случае двумерного массива базовый адрес может содержать начальный адрес массива, а значения сдвига и индексного регистра могут содержать смещения по строке и столбцу. Допустимыми форматами команд являются следующие записи: MOV AН, [BX+2+DI], MOV AН, [DI+BX+2], MOV AН, [BX+2][DI], MOV AН, [BX+2+DI].
МЕТОДИКА ВЫПОЛНЕНИЯ РАБОТЫ
Задание на программирование предполагает разработку программы перемещения значений из одного вектора в другой, возможно, с заменой перемещаемых элементов на заданные значения. Перемещение осуществляется командой MOV приемник, источник. Mov – это основная команда пересылки данных. В ней допустимо большинство из возможных сочетаний операндов: Mov ax,table;пересылка из памяти в регистр, Mov table, ax; пересылка из регистра в память, Mov ds, ax;пересылка между 16-битовыми регистрами Mov bl,al; пересылка между 8-битовыми регистрами, Mov cl,- 30;пересылка константы в регистр, замена значения, Mov [BX],word ptr 25h; пересылка константы в память. Чтобы запрограммировать перебор элементов вектора в случае использования адресации с базированием или индексированием, необходимо предусмотреть изменение смещений в базовом регистре или регистре смещения. Такие изменения часто программируют с использованием операций инкремента и декремента: INC регистр DEC регистр. Команда INC увеличивает на единицу значение операнда (аналогична k++ в языке СИ++), а команда DEC уменьшает значение операнда на 1 (аналог k--в CИ++). Для варианта косвенной адресации зашлите адреса начала массивов в базовый и индексный регистры, например командами: MOV BX, OFFSET SOURCE и MOV DI, OFFSET DEST, и далее используйте регистры BX и DI для адресации элементов массивов.
ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ
1. Изучить приведенный теоретический материал к лабораторной работе, для выяснения возникающих вопросов нужно обратиться к лекциям или литературе. 2. Используя индексную и косвенную адресацию (по заданию преподавателя), разработать программу на ассемблере, выполняющую заданные алгоритмы преобразований. 3. Оттранслировать и скомпоновать программу в исполнимый код. 4. Провести отладку программы и проверить получаемые результаты.
Требования к содержанию и оформлению отчета
Отчет должен включать: а) титульный лист; б) формулировку задачи работы; в) описание результатов выполнения пунктов 2-4: • листинги программ; • результаты выполнения программ; г) вывод, согласованный с задачей работы.
Критерии результативности выполнения работы
Лабораторная работа считается успешно выполненной, если: · отчет на бумажном носителе содержит все необходимые пункты согласно требованиям; · студент дает аргументированные пояснения по всем приведенным в отчете пунктам задания; · студент отвечает на контрольные вопросы преподавателя.
Контрольные вопросы 1. Каков синтаксис команд ассемблера? 2. Что такое исполнительный адрес и как он используется при определении физического адреса операнда? 3. В чем заключается различие прямых и косвенных режимов адресации? 4. Как различить в командах ассемблера прямые и косвенные режимы адресации? 5. Какие режимы адресации Вы знаете?
Варианты заданий 1. В векторе однобайтовых значений SOURCE заменить, используя косвенную адресацию, старший полубайт значений на F (если начальное значение в SOURCE равно 0A, то конечное значение д.б. FA). Подсказка: для перехода к следующему элементу вектора воспользуйтесь командой инкремента INC, преобразование данных выполните с использованием команды OR. 2. В векторе двухбайтовых значений SOURCE заменить, используя прямую адресацию с индексированием, младший байт значений на 0 (если начальное значение в SOURCE равно АААА, то конечное значение д.б. AА00). Подсказка: для перехода к следующему элементу вектора воспользуйтесь командой инкремента INC, преобразование данных выполните с использованием команды AND. 3. Из заданного вектора однобайтовых значений SOURCE переместить, используя прямую адресацию с индексированием, данные в область DEST, поменяв местами старший и младший полубайты (если начальное значение в SOURCE равно AB, то конечное значение д.б. BA). Подсказка: для перехода к следующему элементу вектора воспользуйтесь командой инкремента INC, преобразование данных выполните с использованием команды циклического сдвига. 4. Из заданного вектора двухбайтовых значений SOURCE переместить, используя косвенную адресацию, данные в область DEST, поменяв местами старшее и младшее слово (если начальное значение в SOURCE равно AАBВ, то конечное значение д.б. BВAА). Подсказка: для перехода к следующему элементу вектора воспользуйтесь командой инкремента INC, преобразование данных выполните с использованием команды XCHG. 5. Из заданного вектора однобайтовых значений SOURCE переместить, используя прямую адресацию с индексированием, данные в область DEST, преобразовав все исходные значения в нечетные (если начальное значение в SOURCE равно AB, то конечное значение д.б. AВ, а если исходное значение равно AC, то конечное значение равно AD). Подсказка: для перехода к следующему элементу вектора воспользуйтесь командой инкремента INC, преобразование данных выполните с использованием команды OR. 6. В заданном векторе двухбайтовых значений SOURCE, используя косвенную адресацию, преобразовать все исходные значения в четные (если начальное значение в SOURCE равно AB, то конечное значение д.б. AА, а если исходное значение равно AC, то конечное значение равно AС). Подсказка: для перехода к следующему элементу вектора воспользуйтесь командой инкремента INC, преобразование данных выполните с использованием команды AND. 7. Из заданного вектора байтов SOURCE, используя косвенную адресацию, переместить в область DEST каждый второй байт. Подсказка: для перехода к каждому второму байту дважды воспользуйтесь командой инкремента, а для перехода следующему элементу вектора DEST воспользуйтесь командой инкремента INC один раз. 8. Из заданного вектора слов SOURCE переместить, используя прямую адресацию с индексированием, данные в область DEST, обнулив в исходных значениях второй и третий полубайты (если начальное значение в SOURCE равно AАBВ, то конечное значение д.б. A00В). Подсказка: для перехода к следующему элементу вектора воспользуйтесь командой инкремента INC, преобразование данных выполните с использованием команды AND.
ЛАБОРАТОРНАЯ РАБОТА № 3 ПРОГРАММИРОВАНИЕ ВЕТВЛЕНИЙ ЦЕЛЬ И ЗАДАЧА РАБОТЫ
Целью работы является закрепление знаний по командам условного и безусловного переходов, закрепление умения разработки схем программ, а также приобретение начальных навыков разработки алгоритмов и реализации программ на Ассемблере. Задача работы заключается в разработке схемы и программы обработки символьной информации с заданным преобразованием данных.
2. ОСНОВНЫЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ Команды условного перехода
Команды передачи управления реализуют изменение естественного порядка выполнения команд программы. В мнемонические обозначения команд условного перехода входят буквы, которые определяют условия в соответствии с таблицей 2. В этой таблице операнд “метка перехода” или “близкая метка” отражает тот факт, что помеченная команда должна находиться в пределах текущего сегмента кода и на относительном расстоянии от команды перехода больше -128 и меньше 127 байтов. Таблица 2 Мнемокоды команд условного перехода
Решение о том, куда будет передано управление командой условного перехода, принимается на основании условия. Источниками таких условий могут быть: - любая команда, изменяющая состояние флагов OF, CF,SF, ZF; - команда сравнения CMP. Формат команды CMP: CMP приемник, источник или CMP операнд1, операнд2. Эта команда осуществляет вычитание (операнд1 - операнд2) или (приемник – источник), однако результат никуда не записывается, а только устанавливает флаги в соответствие с таблицей 3. Таблица 3 Значения флагов, устанавливаемые командой CMP
В этой таблице приняты следующие обозначения: - “H” означает, что ‘не имеет значения” или иначе, на этот флаг операция не влияет; - 0/1 означает, что флаг устанавливается или в 1 или в 0 в зависимости от значений операндов (отрицательные или положительные или разнознаковые операнды сравниваются). В таблице 4 отражается действие команд условного перехода по значениям анализируемых этими командами флагов. В этой таблице через слеш ‘/’ перечисляются идентичные команды, действие которых совершенно одинаково, и применение конкретной из них зависит от пристрастий программиста. Наличие идентичных команд объясняется тем фактом, что если число_1>число_2, то можно с уверенностью утверждать, что число_1 не (меньше или равно) число_2.
Таблица 4 Логика команд условного перехода
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Последнее изменение этой страницы: 2017-02-17; просмотров: 606; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.143.237.54 (0.011 с.) |