| 29.05.2013, 11:38 | |
|
Ответы с готовыми решениями:
65
Организация тем в разделе Assembler, MASM, TASM Полезные макросы для MASM и TASM |
|
608 / 406 / 8
Регистрация: 26.04.2012
Сообщений: 2,065
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 29.05.2013, 19:16 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Написать программу, выводящую строку "Hello, World!" на экран
извини, что воспользовался твоим топиком, но первое сообщение в топике будет таскаться за ним во всех страницах
5
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Эксперт быдлокодинга
2094 / 528 / 70
Регистрация: 04.11.2010
Сообщений: 1,314
|
||||||
| 29.05.2013, 19:22 | ||||||
|
Вывод спирали на экран в DOS Теория Архимедова спираль — спираль, плоская кривая, траектория точки M, которая равномерно движется вдоль луча OV с началом в O, в то время как сам луч OV равномерно вращается вокруг O. Другими словами, расстояние Уравнение Архимедовой спирали в полярной системе координат записывается так: Повороту прямой на При вращении луча против часовой стрелки получается правая спираль, при вращении — по часовой стрелке — левая спираль. Обе ветви спирали (правая и левая) описываются одним уравнением (1). Положительным значениям Луч OV, проведенный из начальной точки O, пересекает спираль бесконечное число раз — точки B, M, A и так далее. Расстояния между точками B и M, M и A равны шагу спирали Практика Две пересекающиеся спирали. X и Y рассчитываются по формулам:
4
|
||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 30.05.2013, 05:04 [ТС] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Синус на ассемблере Вычислить синус на ассемблере можно несколькими способами, выбирайте тот, который вам больше подходит:
3
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 30.05.2013, 06:06 [ТС] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
DEBUG.EXE взято здесьстатья winsoft Стандартный отладчик debug.exe, входит в любую версию DOS/Windows. debug.exe прост, доступен и подходит для начинающих программистов на языке Ассемблер. Для запуска debug.exe заходим в Пуск->Выполнить и набираем команду debug. Команды debug.exe Для начала разберемся с правилами набора команд debug.exe:
Разобравшись с правилами, переходим к изучению команд debug.exe. Замечу, что работа с командами debug.exe в чем-то похожа на работу с командной строкой DOS. После загрузки отладчика на экране появится приглашение, выглядящее в виде дефиса:
Команды debug.exe вводятся сразу после приглашения на месте, которое отмечено курсором. Каждая команда состоит из идентификатора и параметров, идентификатор состоит из одной буквы. таблица команд debug.exe
Просмотр областей памяти В этой части рассмотрена работа команды D, позволяющей просматривать содержимое отдельных областей памяти.Этот пример использует команду D для просмотра области памяти, начиная с 0159:0240:
Адрес, указанный в строке, относится исключительно к первому байту в параграфе, а адреса последующих байтов следует вычислять самостоятельно. Шестнадцатеричное представление содержит по два знака в каждом байте, а сами байты разделены пробелами для облегчения чтения. Кроме того, следует отметить, что восьмой и девятый байты разделены дефисом, разделяя тем самым параграф на две части и облегчая вычисление адресов байтов в параграфе. Полезные приемы с командой D
Непосредственный ввод программы в память с помощью debug.exe debug.exe позволяет вводить программу непосредственно в память машины, а затем следить и управлять ее выполнением. Мы будем вводить программу в машинных кодах, используя команду E. При этом будьте бдительны - ввод ошибочных данных по ошибочному адресу чреват непредсказуемыми последствиями! Хотя к серьезным проблемам в системе это вряд ли приведет, но потерять все данные, введенные в debug.exe, можно потерять запросто.Программа, которую мы сейчас будем вводить, использует данные, заложенные непосредственно в теле инструкций. Далее показан листинг программы на Ассемблере, в комментариях указаны аналоги команд языка в машинных кодах, а также объяснение каждой команды. Замечу, что в числах нет символа h, поскольку, как было сказано выше, debug.exe понимает только числа в шестнадцатеричной системе.
Теперь можно ввести программу в память. Разделим машинный код на три части по шесть байт и введем каждую, используя команду E и начиная с адреса CS:100.
Здесь же указаны и значения флагов переполнения, направления, прерывания, знака, нуля, дополнительного переноса, четности и переноса:
После этого debug.exe снова выводит информацию о регистрах:
Так, раз за разом выполняя команду T, мы дойдем до последней инструкции JMP 100. Она установит регистр IP в 100h, и debug.exe вернется к началу программы. Возвращаясь к началу программы, следует заметить, что в DS, ES, SS и CS содержится один и тот же адрес. Дело в том, что debug.exe рассматривает введенные программы исключительно как программы .COM. А в программах .COM, в отличие от .EXE, стек, код и данные хранятся в одном сегменте. Ассемблирование и дизассемблирование В прошлом примере мы вводили программу в машинных кодах, однако, debug.exe вполне способен понимать инструкции, записанные на ассемблере. Для работы с такими программами в debug.exe используются команды A и U.Команда A запрашивает инструкции на ассемблере и преобразовывает их в машинный код. Для начала инициализируем начальный адрес для ввода инструкций (100h):
Перейдем к процедуре дизассемблирования, а в качестве примера возьмем только что введенную программу. Используем адреса первой и последней инструкций для указания диапазона, который мы собираемся дизассемблировать, т.е. 100h и 107h.
Итог А теперь, после небольшого обзора возможностей стандартного отладчика debug.exe давайте подведем итоги. Итак:
Фрагменты из статьи "низкоуровневое программирование для дZенствующих - DZebug: руководство юZверя"
Фрагменты статей Рустэма Галеева aka Roustem взято здесьWin32 в машинных кодах Инструменты Чтобы вводить двоичные значения в компьютер, необходим шестнадцатеричный редактор. Поскольку мы решили обходиться стандартными средствами, имеющимися в любой типичной поставке Windows, используем в качестве шестнадцатеричного редактора старый досовский отладчик debug. Рассмотрим лишь те возможности этого отладчика, которые нам понадобятся в работе. Сначала имеет смысл создать отдельную папку для проводимых экспериментов, например, \exp. Теперь запустим командную строку DOS, перейдем в созданный каталог (cd \exp) и наберем: debug. Появляется черточка - приглашение отладчка; можно набирать команды. Сразу о том, как завершить работу debug: для этого служит команда q (quit). Debug позволяет создавать и записывать на диск файлы, но у этого процесса есть некоторые особенности. Дело в том, что создаваемые файлы будут в старом досовском формате com. Для нас это означает, что при записи на диск отладчик использует данные, начиная со смещения 100h кодового сегмента (адрес которого содержится в регистре CS), это надо учитывать. Если наши данные будут начинаться со смещения 0, первые 256 (100h) байтов окажутся утерянными (для содержимого регистров CS и DS по умолчанию). Либо надо вручную изменить (увеличить на 10h) значение регистра DS. Попробуем создать простейший файл. Запускаем debug. Для записи служит команда w (write); однако вначале должно быть определено имя файла с помощью команды n (name). В принципе, имя может быть любым досовским именем (в коротком формате 8.3), но расширение не может быть exe или hex. Лучше использовать расширение bin, а потом переименовать файл. Набираем:
Перейдем к формированию наших данных. Одна из полезных команд - f (fill), она позволяет заполнить участок памяти указанными данными. После f первым параметром идет смещение (начальный адрес) заполняемого блока, затем либо параметр l (length) и число, указывающее на длину заполняемого участка в байтах, либо смещение его конца. После этого - собственно данные, которыми будет заполняться данный участок. Причем данные могут быть как в виде 16-ричных чисел, так и в виде заключенных в апострофы или кавычки строк, причем их можно чередовать. Например, заполним первые 256 (100h) байт строкой "This is the filling string":
Теперь проделаем эксперимент, демонстрирующий особенность сохранения файлов в debug. Создадим файл, первые 100h байт которого заполнены символами '0', вторые 100h байт - символами '1' и т.д. до, скажем, '9'. Дадим файлу имя 'first.txt' (или любое другое с расширением .txt), а размер его будет a00h (2,5 Кб).
Открываем 'first.txt' в Блокноте. Но что это? Файл начинается с единиц, а в конце какой-то мусор? Смотрим в debug'е: d 0 ff - все нормально, заполнено цифрами 0 (30h). Вот это и есть та особенность отладчика, о которой мы говорили в начале. В файл записываются данные начиная со смещения 100h относительно кодового сегмента. Исправить эту ситуацию можно попытаться двумя способами. Рассмотрим еще одну команду отладчика: m (move). Она позволяет копировать данные из одной области памяти в другую. Первый параметр, как и ранее, является смещением начала участка памяти, который необходимо скопировать, второй - либо смещением конца копируемого участка, либо (при наличии буквы l) его длиной, третий параметр - смещение места назначения, куда надо скопировать данные. С помощью этой команды мы можем "передвинуть" весь наш блок данных так, чтобы он начинался со смещения 100h:
Второй способ - изменить значение регистра сегмента данных DS таким образом, чтобы он указывал на область со смещением 100h относительно начала кодового сегмента. Т.е. надо просто добавить к старому значению DS 10h. Допустим, в DS было значение 2020. Изменим его на 2030:
Команда e (enter) позволяет вводить данные по конкретным адресам. Первый параметр указывает начальный адрес, остальные рассматриваются как данные для ввода. Причем здесь тоже можно использовать как 16-ричные числа, так и символьные строки, чередуя их между собой произвольным образом. В связи с данной командой рассмотрим особенность процессоров IA-32, о которой говорилось в прошлой статье. Речь идет об "обратном" представлении чисел в памяти; хотя по внимательном рассмотрении этого вопроса представление чисел в процессорах IA-32 как раз является естественным ("нормальным"), а "обратным" оказывается наша традиционная запись. Попробуем разобраться. Мы читаем и записываем слева направо. Если записать порядковые номера, они будут увеличиваться тоже слева направо. Естественно таким же образом нумеровать объекты, скажем, байты памяти: 1, 2, 3, 4 и т.д. Значения возрастают слева направо. Теперь посмотрите на числа, у которых увеличиваются разряды: 1, 10, 100, 1000. Каждый новый разряд мы добавляем слева, т.е. возрастание числа получается справа налево - порядок, противоположный традиционному письму. Если сохранять в памяти текст, т.е. строку символов, при добавлении новых символов они будут помещаться "правее", т.е. по возрастающим адресам памяти (поскольку мы нумеруем их слева направо). А как быть, если увеличивается значение числа и оно перестает помещаться на старом месте? Скажем, вместо байта требуется уже слово (два байта)? Новый байт можно добавить "слева" (с меньшим адресом) или "справа" (с большим адресом). Поскольку адресом многобайтной конструкции по соглашению считают самый младший адрес, он может указывать либо на байт, в котором хранятся старшие разряды числа, либо на байт, в котором хранятся младшие разряды. Первый способ называется "big-endian", второй - "little-endian". Так вот, в процессорах IA-32 используется "little-endian", т.е. старшие разряды добавляются "справа" (по старшим адресам памяти) - порядок, обратный нашей записи чисел. Говорят, в свое время Фибоначчи, заимствуя цифры у арабов, не учел особенностей их письма: арабы пишут справа налево, в отличие от нас. И так же располагались разряды их цифр. Фибоначчи использовал тот же порядок, хотя европейцы писали в обратном направлении - вот где корень всех наших бед .Таким образом, если мы хотим разместить по адресу 10h число 12h, мы набираем:
Как вы уже, очевидно, заметили, в 16-разрядной системе используется сегментная модель памяти. Это создает дополнительные проблемы; в частности, команды заполнения (f) и перемещения (m) не работают через границы сегментов. Поэтому, хотя debug в принципе позволяет сохранять файлы размером более одного 16-разрядного сегмента (64 Кб), при составлении таких файлов у нас могут возникнуть проблемы. Их можно решить другим путем - собирая в debug отдельные "модули", не превышающие 64 Кб, и соединяя их с помощью команды DOS copy. Для доказательства такой возможности соберем простой текстовый файл размером в 1 Мб. Собирать будем из 16 модулей в 64 Кб, сохраненных средствами debug; каждый модуль будет заполнен единственным символом - 16-ричной цифрой, значение которой равно номеру модуля (для контроля). Сначала настроим регистр DS (если он не был настроен ранее), увеличив его значение на 10h. В регистр CX должно быть значение 0, в BX - 1 (это соответствует размеру файла 10000h байт, или ровно 64 Кб):
Осталось рассмотреть лишь некоторые методы автоматизации нашей работы. Работать с debug, все время вводя данные в интерактивном режиме, может оказаться утомительным - удобнее использовать заранее подготовленные шаблоны, внося в них каждый раз небольшие изменения. Для этого воспользуемся еще одной возможностью ОС - перенаправлением ввода-вывода. Все необходимые команды для debug записываются в текстовый файл, который затем подается на вход отладчика при его запуске следующим образом: debug < batch.txt Для испытания этого способа повторим тот же алгоритм, который мы использовали при создании файла "first.txt". В Блокноте создаем файл "batch.txt" со следующим содержимым:
Наконец, рассмотрим еще одну команду - a (assemble). Эта команда позволяет войти в режим ассемблирования, т.е. ввода инструкций на ассемблере, которые debug автоматически преобразует в машинные коды. Однако делает это он в 16-разрядном режиме, что нам совершенно не подходит. Но мы можем воспользоваться в этом режиме директивой db, позволяющей вводить отдельные байты, как в команде e. Это может напомнить путешествие из Петербурга в Москву через Владивосток; однако, удобство этого метода в том, что отладчик будет автоматически подсчитывать смещение следующей вводимой инструкции (в нашем случае - байта), и можно не считать все самим. Параметром команды a является адрес (смещение), с которого мы начинаем вводить инструкции. Чтобы выйти из режима ассемблирования, необходимо просто нажать 'Enter' еще раз (в тексте пакетного файла в этом месте должна быть пустая строка). Потренируемся в использовании этой команды с использованием инструкций в машинных кодах, которые мы составляли в прошлый раз (впрочем, ничто не мешает составить и новые). Сначала поработаем в интерактивном режиме:
Но преимущества режима ассемблирования станут очевидными при работе с перенаправлениями. Создадим файл "second.txt" и наберем в нем те же данные (не забыв про пустую строку в соответствующем месте и команду q в конце). В командной строке DOS запишем:
Завершим знакомство с отладчиком способами загрузки созданных заранее шаблонов. Для загрузки файлов служит команда L (Load). При этом имя файла должно быть уже указано командой n. Файл загружается по смещению 100h. Либо имя файла можно указать в качестве параметра при вызове отладчика:
Для примера рассмотрим, как можно загрузить в качестве шаблона созданный ранее файл "first.txt" и сохранить его после сделанных изменений. Сначала создаем "автоматизирующий" файл с командами ("third.txt"):
Финальный штрих - полная автоматизация создания исполняемого файла. Для этого создается bat-файл, в котором записываются вызов самого debug, а также другие необходимые действия, например, составление одного большого файла из отдельных модулей с помощью команды copy или переименование файла с расширением bin в файл с расширением exe. Создадим в Блокноте файл "make.bat":
Каков же итог? Любой файл - будь то картинка, векторная или трехмерная графика, музыка, видео или исполняемый - это всего лишь сохраненный набор двоичных чисел. А итог таков, что мы умеем теперь создавать файлы практически любого размера и с любым содержанием. Единственное, что при этом надо - это изучить формат соответствующего типа файла. Этим мы и займемся в следующей статье применительно к исполняемым файлам Windows.
12
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 30.05.2013, 10:22 [ТС] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Исполняемые файлы Windows Как сделать, чтобы программа заработала? Работа приложения начинается с того, что операционная система создает процесс. Это не просто загруженная в память программа пользователя; процесс предполагает создание множества внутренних системных структур для обеспечения работы программы и предоставления ей различных ресурсов, таких как память, процессорное время, доступ к установленному в системе оборудованию и так далее.Важнейшим ресурсом являетcя виртуальная память. Каждый процесс получает в свое распоряжение собственное виртуальное адресное пространство памяти размером 4 Гб. Это значит, что он может обращаться по любому из адресов памяти от 0 до FFFFFFFFh. Но это значит также и то, что различные процессы могут использовать одни и те же адреса, не мешая друг другу. Система работает с памятью в виде блоков фиксированного размера, называемых страницами (обычно по 4 Кб; на современных процессорах могут быть страницы также по 2 Мб) и использует страничную переадресацию для отображения одних и тех же виртуальных адресов различных процессов в разные области физической памяти. Кроме того, при недостатке физической памяти временно неиспользуемые данные могут сохраняться на диске, освобождая физическую память для других виртуальных адресов (это называется подкачкой). В адресном пространстве процесса резервируются области для динамически выделяемой памяти ("кучи") и стека (о нем мы подробнее поговорим в следующей статье). Затем образ программы загружается из файла на диск по базовому адресу загрузки. Образ программы состоит из одной или нескольких секций. Для каждой секции выделяется несколько страниц памяти, имеющих одинаковые атрибуты. Например, это могут быть исполняемые страницы, страницы только для чтения или для чтения и записи. Это сделано для уменьшения количества возможных ошибок; например, случайный запуск на исполнение страницы, содержащей не код, а данные, может привести к непредсказуемым результатом. Если же в атрибутах страницы не указана возможность исполнения, это приведет к сообщению об ошибке. Точно так же атрибут "только для чтения" позволяет перехватить попытку случайной или преднамеренной записи на страницу, содержание которой не должно изменяться (допустим, если она содержит константы). Расширение "exe" осталось в наследство от старых DOS-овских исполняемых (executable) файлов. Используемый в настоящее время формат исполняемых файлов Windows называется "Portable Executable" (PE), поскольку один и тот же формат используется для разных платформ. Более того, он построен на основе шаблонов, являющихся общими и для объектных файлов формата COFF (используемых в том числе в мире Unix), а также построенных на их основе библиотечных файлов и файлов импорта (.lib). Формат PE в системе Win32 является универсальным: его используют не только исполняемые файлы (exe), но и динамические библиотеки (dll) и их особые разновидности -элементы ActiveX (ocx) и системные драйверы (sys и drv). Как и старый формат exe для DOS, PE-файл состоит из заголовка и собственно образа исполняемой программы. Образ программы, как уже отмечалось, может быть составлен из одной или нескольких секций. Заголовок же можно условно разделить на "старый" (DOS-заголовок и DOS-стаб) и "новый" (все остальное) "Новый" заголовок составлен из собственно PE-заголовка и таблицы секций, которая фактически является картой отображения записанных в файле секций образа программы в память. В PE-заголовке выделяют также несколько составных частей, но для нашего рассмотрения они несущественны. Отметим лишь каталог смещений-размеров, который указывает на расположение и размеры специальных служебных таблиц. Для размещения последних могут быть выделены отдельные секции в образе программы, но это не является обязательным; в принципе, для всей программы можно использовать одну единственную секцию, разместив в ней и данные, и код, и все необходимые вспомогательные структуры. Теперь рассмотрим все подробнее. Поскольку попытка запуска создаваемых нами программ под DOS маловероятна, можно без особых проблем обойтись без программы-заглушки DOS. PE-заголовок в этом случае будет следовать сразу за старым заголовком DOS, а именно - непосредственно после 4-байтного поля со смещением 3Ch, то есть по смещению 40h (само поле 3Ch будет содержать в данном случае это же значение). Единственное, что нужно еще оставить в старом заголовке - это сигнатуру в виде 2 ASCII-символов 'MZ' в начале файла (байты 4Dh 5Ah). Остальные поля могут содержать нули - загрузчик Windows их не использует. Поля PE-заголовка приведены в таблице 1. Смещения указаны относительно начала заголовка, а жирным шрифтом выделены те поля, при неверных значениях которых Windows откажется загружать программу. Остальные поля либо содержат необязательные данные (например, указатель на размещение и размер отладочных данных), либо для них предусмотрены значения по умолчанию (как для размеров кучи и стека), либо используются лишь для определенных видов файлов (например, флаги dll или контрольная сумма). Таблица 1. PE-заголовок
Таблица секций следует непосредственно после PE-заголовка (после каталога смещений). Каждый вход таблицы имееет следующий формат (см. табл. 2). Таблица 2. Строка таблицы секций
Секция может содержать т.н. неинициализированные данные. Фактически, это просто резервирование определенных адресов памяти под будущие переменные. Для таких данных место в файле не отводится; память резервируется лишь при загрузке на исполнение. Если вся секция содержит лишь неинициализированные данные, поля размера данных секции в файле и смещения начала данных секции в файле равны нулю. В любом случае, когда размер секции в файле меньше указанного размера секции в памяти, остаток заполняется до нужного размера нулями. Поле флагов секции - то самое, где задаются атрибуты страниц памяти, отводимых под секцию. Возможно использование до 32 флагов (по одному на каждый бит 4-байтного значения), но часть из них зарезервирована, другая часть используется лишь в объектных файлах. Биты нумеруются от младшего к старшему, начиная от 0 (самый младший бит - 0, самый старший - 31). Наиболее употребительные для исполняемых файлов следующие:
Секции исполняемого файла Следующей будет секция с данными только для чтения; назовем ее '.rdata'. Она будет расположена в файле по смещению 400h, а в памяти - по смещению 2000h. Флаги: 40000040h. За ней - секция данных с разрешениями на чтение и запись: '.data', расположение в файле - 600h, в памяти - 3000h; флаги: C0000040h.Теперь составим командный файл для отладчика debug. Имеет смысл сначала создать специальную папку "Шаблоны". В ней сохраним этот файл для использования в дальнейшем. Открываем Блокнот и набираем:
А вот дальше пойдут поля PE-заголовка, которые нужно будет настраивать для каждого отдельного exe-файла. Чтобы было удобнее редактировать этот файл в дальнейшем, оставим здесь комментарии - а для этого нам придется изменить способ ввода и перейти в режим ассемблирования.
Имеет смысл также выделить те участки, которые нужно будет в дальнейшем редактировать (как этот случай - число секций может каждый раз быть разным); для этого удобно выделять каким-либо способом строку с комментарием, чтобы она сразу бросалась в глаза. Оставшуюся часть файла для debug приведем, как есть; она не должна вызвать проблем (обратите внимание на пустые строки - их нельзя удалять; и помните про обратный порядок байтов в числах, требующих более 1 байта):
Теперь у нас есть шаблон, который можно вставлять в начало exe-файла с 3 секциями, размеры которых не превышают 200h байт каждая. Чтобы протестировать его, нужно собрать "настоящий" exe-файл с его использованием. Для этого немного схитрим: вставим две пустые секции (содержащие лишь нули) в качестве секций данных; а в секции кода используем всего 2 байта: EB FE. Это инструкция, передающая управление на себя (как мы узнаем в дальнейшем). Т.е. наша программа просто зацикливается; но пока нам большего и не надо. В блокноте создадим еще 2 простых файла. Первый - "s1.txt" (содержит наш "код"):
Второй вызов debug исполняет команды в файле s1.txt, создавая файл s1.bin с нашей "секцией кода". Перенаправление с двумя знаками >> означает, что отчет записывается не с начала указанного файла (затирая его содержимое), а добавляется в его конец. Третий вызов debug выполняет s2.txt, создавая пустую секцию в файле s2.bin. Наконец, мы объединяем эти секции в единый файл с расширением exe, причем заметьте - файл s2.bin использован дважды (2 пустые секции). Теперь полученный файл можно попытаться запустить. Но перед этим неплохо бы еще раз тщательно проверить все исходные файлы - вероятность допущенной ошибки довольно велика. Просмотрите файл report.lst - нет ли сообщений отладчика об ошибках. В частности, типичной ошибкой является случайное использование в командах вместо латинских букв кириллицы (особенно одинаковых - c и с, e и е и т.д.) Если файл создан правильно, ничего не произойдет - сообщения Windows будут лишь при наличии ошибки. Зато нажав Ctl-Alt-Del, вы увидите исполняющуюся задачу 'nil'. Выделите ее и нажмите кнопку "Завершить процесс" - пока мы можем закрыть эту программу только таким способом. Простейшее приложение В первой статье мы получили представление о строении машинных инструкций. Во второй статье научились составлять с использованием подручных средств файлы любого уровня сложности. Наконец, в прошлой статье начали создавать исполняемые файлы, которые система "признает" в качестве своих. Теперь мы вплотную стоим перед дверями, открывающими доступ к неисчислимым сокровищам мира Windows.Это API - Application Programming Interface - интерфейс прикладного программирования, огромная библиотека уже готовых наборов инструкций, входящая в состав самой системы и служащая для выполнения разнообразнейших задач почти на все случаи жизни - имеется в виду, жизни в мире Windows .Сокровища эти упакованы в виде процедур и функций и размещены в системных модулях - dll. Чтобы получить к ним доступ, необходимо связаться с соответствующими модулями и импортировать из них нужные нам функции. Попробуем сегодня создать элементарное приложение, выводящее простое сообщение с набранным нами текстом и на этом завершающее свою работу. Эту работу осуществляет функция MessageBoxA, находящаяся в модуле User32.dll. Чтобы не пришлось "прибивать" наше приложение из менеджера задач, как в прошлый раз, оно должно также содержать для своего нормального завершения вызов функции ExitProcess из модуля Kernel32.dll. Обычно для своей работы эти системные функции используют какие-то наши данные, которые мы им должны передать в виде параметров. Например, функции MessageBoxA мы должны предоставить текст, который хотим отобразить. В первой статье мы рассматривали инструкции, позволяющие копировать данные из одного места в другое. Однако, в данном случае мы ничего не знаем о внутреннем "устройстве" вызываемых функций и о том, в каком месте они хранят данные, с которыми работают. Как раз для подобных случаев был изобретен в свое время механизм, получивший название стека. Стек - это некоторая область в виртуальном адресном пространстве процесса, специально предназначенная для временного хранения данных. Если помните, при создании PE-заголовка мы заполняли поля с размером выделяемого стека. Место, где разместить стек, выбирает система, а соответствующий адрес памяти сохраняет в регистре ESP. Этот регистр специально предназначен для работы со стеком и поэтому не должен использоваться ни для каких других данных. Доступ же к стеку осуществляется последовательным образом, причем операнд всегда должен иметь размер в 4 байта (для 32-разрядного режима). Для записи очередного числа значение ESP уменьшается на 4 и указывает на "чистую" область размером в 4 байта. В эту область и копируется единственный операнд соответствующей инструкции. При извлечении сохраненного числа из стека копируется 4-байтное значение, адрес которого находится в настоящий момент в ESP, а затем значение ESP увеличивается на 4 и указывает уже на предыдущее сохраненное число. Единственное, за чем необходимо следить - чтобы каждому случаю помещения данных в стек соответствовал ровно один случай извлечения этих данных из стека (это называется сбалансированностью стека). Рассмотрим инструкции помещения данных в стек (инструкции извлечения данных из стека нам пока не понадобятся, и мы займемся ими в другой раз). На ассемблере группа данных инструкций обозначается мнемоникой PUSH. Сначала инструкция помещения в стек непосредственного значения:
Наконец, еще одна инструкция, использующая уже знакомый нам по первой статье байт ModR/M. Этот байт позволяет записывать в стек значения, хранящиеся в памяти. Но в данном случае есть одна особенность использования этого байта. Вспомните, что байт ModR/M предполагает наличие двух операндов (один из которых всегда находится в регистре). Здесь же необходимо указывать лишь один операнд - другой все время один и тот же и задается неявно (адрес в ESP). Поэтому поле REG байта ModR/M, служившее для обозначения регистра, теперь используется в качестве расширения опкода и для данной конкретной инструкции всегда одно и то же (постоянно). А сама инструкция вместе с байтом ModR/M выглядит так:
Флаги стиля MessageBoxA Поля структуры могут содержать следующие значения, определяющие внешний вид и поведение окна сообщения. Тип окна:
Таким образом, число 0 в качестве стиля означает простое окно сообщения с одной кнопкой 'OK' и без всяких значков. Инструкция для помещения 0 в стек нам уже знакома: 6Ah 00h. Вторым в стек должен быть помещен адрес начала строки, являющейся заголовком окна сообщения. Эту строку разместим в секции данных нашей программы; используя ту же схему, что и в прошлый раз, это будет третья секция, начинающаяся со смещения 3000h относительно базового адреса загрузки, который равен 400000h. В качестве заголовка можно выбрать любой текст; пусть это будет просто "Заголовок". В конце строки обязательно должен быть нулевой байт. В результате адрес нашей строки в памяти будет 403000h; поместим это непосредственное значение в стек (на этот раз мы указываем все 4 байта, поэтому бит s = 0): 68 00 30 40 00 (h). Третьим в стек помещается адрес начала другой строки, которая является собственно выводимым сообщением. Пусть будет "Текст сообщения для вывода"; расположим ее сразу после первой строки (после ее завершающего 0) - по адресу 4030A0h: 68 A0 30 40 00 (h). Последний параметр для функции MessageBoxA - описатель (handle) другого окна, являющегося владельцем нашего окна сообщения. Если такое окно имеется, оно (в зависимости от значения в поле "Режим") может быть "заморожено" на время отображения нашего окна сообщения. В данном случае других окон в приложении нет, поэтому оставим 0: 6A 00 (h). Функция ExitProcess принимает единственный аргумент - т.н. код завершения. Он обычно равен 0 при нормальном завершении приложения. Мы также оставим это значение: 6A 00 (h). Больше данных в нашем приложении не предвидится; поэтому мы можем сразу построить секцию данных. Создадим отдельную папку и разместим в ней файл "data.txt" со следующим содержимым:
Но существуют отклонения от этого последовательного хода исполнения; одним из таких случаев является вызов функций. В этом случае содержимое регистра EIP автоматически сохраняется в стеке (это значение называется адресом возврата), а в регистр EIP заносится операнд инструкции вызова функции, который является адресом первой команды вызываемой функции. В свою очередь, последней командой функции должна быть инструкция возврата управления, которая восстанавливает сохраненный ранее в стеке адрес возврата в регистре EIP, и управление переходит на следующую после вызова функции инструкцию. В результате вызов функции выглядит так, будто это обычная одиночная инструкция и не было всего этого "путешествия" куда-то в другие области адресного пространства. Рассмотрим инструкции вызова функций (эта группа обозначается на ассемблере мнемоникой CALL). Инструкция с непосредственным смещением:
Есть также инструкция с косвенной адресацией; она использует байт ModR/M:
Хорошо; но как получить адрес нужной нам функции? Это делается посредством процесса, который называется импортом. Загрузчик Windows осуществляет его автоматически при запуске приложения, нужно только указать ему, какие функции и из каких модулей нам потребуются. Вот для этой цели и служат таблица импорта и сопутствующие ей данные, о которых упоминалось в прошлой статье. Теперь познакомимся с ними поближе. Число записей в таблице импорта равно числу импортируемых модулей. Последняя запись должна содержать нули для обозначения конца таблицы. Каждая запись имеет следующий формат:
Теперь мы можем составить нужные для нашего приложения данные. Разместим их в секции .rdata (со смещением 2000h относительно адреса загрузки). Создадим файл rdata.txt. Это как раз тот случай, когда могут оказаться полезными два прохода, чтобы узнать относительные взаимные смещения различных таблиц. Учтите, что все смещения должны указываться относительно базового адреса загрузки. Я приведу здесь уже готовый вариант:
Составим файл code.txt:
Если же все нормально - хлопайте в ладоши! Вот оно, наше окно, собственноручно созданное самым честным образом в самых что ни на есть настоящих машинных кодах. Фрагмент из книги Ивана Склярова "Головоломки для хакеров" Создать СОМ-файл (в том числе и вирус) в системе отключенной от сети можно с помощью простейшего консольного отладчика debug.exe, который появился уже в первых версиях MS-DOS и стандартно устанавливается во всей линейке Windows от 9x до 2003. Рассмотрим, как им можно воспользоваться на примере простой программы, которая выводит на экран фразу: "Hello, World!". Чтобы ввести эту программу с помощью debug.exe и создать исполняемый файл в системе, нужно выполнить следующие действия:
5
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 30.05.2013, 11:51 [ТС] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Графика на ассемблере под DOS Представление данных на мониторе компьютера в графическом виде впервые было реализовано в середине 50-х годов для больших ЭВМ, применявшихся в научных и военных исследованиях. С тех пор графический способ изображения данных стал неотъемлемой принадлежностью подавляющего числа компьютерных систем, в особенности персональных.MDA Monochrome Display Adapter был создан для работы с одноцветным (зеленым) дисплеем. Адаптер дисплея преобразует сигналы, распространяющиеся по шине компьютера, к форме, воспринимаемой монитором. MDA является символьной системой, не обеспечивающей никакой другой графики, за исключением расширенного множества символов IBM (символы псевдографики). MDA работал только в одном режиме — вывод 25 строк символов по 80 символов в ряду. Вы помещали символы на экран, записывая коды ASCII в дисплейный буфер. Преобразование из кода ASCII в точки на экране выполнялось аппаратно. Для вывода символа размером 9х14 точек требовалось разрешение (80х9)х(14х25) = 720х350 = 252000 точек на экран. На плате MDA располагалось 4 Кб памяти для записи кодов символов и их атрибутов.CGA Color Display Adapter — первый растровый многорежимный дисплейный адаптер. Использовался и для символьных, и для графических режимов. Содержал 16 Кб памяти, соединяется с монитором 4 сигнальными проводами (синий, красный, зеленый, яркостный), и поэтому мог работать одновременно с 24=16 цветами. Позволяет работать в двух текстовых (монохромный — 25 строк по 80 символов в строке и 16-цветный — 25 строк по 40 символов в строке) и трех графических режимах.
VGA Video Graphics Array содержит 256 Кб до 1 Мб памяти. Позволяет выводить на экран 218=262144 цветовых оттенка, но одновременно на экране могут быть только 256 цветов. Имеет три встроенных ЦАПа. На монитор VGA адаптер отправляет три аналоговых сигнала, которые управляют работой электронных пушек монитора. Поддерживает 17 документированных режимов: 640х480 (монохром), 640х480х16 цветов, 320х200х256 цветов (смотри таблицу ниже) и кучу недокументированных, но также стандартных режимов: 320х400х256 цветов, 360х480х256 цветов и т.д.Некоторые стандартные видеорежимы Нетрудно подсчитать, что режим 640х400х256 цветов использует практически всю 256 Кбайтовую память VGA адаптера (640х400х8 = 2048000 бит = 256000 байт = 250 Кбайт). В то же время многие VGA адаптеры снабжены, как правило, не менее чем 1 Мбайт видеопамяти, а большинство находящихся в эксплуатации имеют не менее 512 Кбайт, что позволяет нам получить в свое распоряжение режимы 640х480х256 цветов (300 Кбайт) и 800х600х256 цветов (469 Кбайт). К большому сожалению, адаптер VGA не поддерживает глубину цвета более 8 разрядов (28=256 цветов).
Super VGA К моменту кончины DOS самый распространенный видеоадаптер. Содержит от 1 Мб и более памяти. Позволяет выводить на экран изображение с максимальным разрешением 1600х1200 и максимальным цветовым разрешением 16 777 216 оттенков. Адаптеры CGA, EGA и VGA программно совместимы всегда и совместимы снизу вверх: программа, написанная для CGA будет работать на EGA, VGA и SVGA, написанная для EGA будет работать на VGA и SVGAВидеоадаптеров SVGA достаточно много и не все они программно совместимы между собой. По-этому для использования в программе видеорежимов Super-VGA необходимо либо производить настройку на конкретный видеоадаптер, либо использовать видеоадаптер в соответствии со стандартом VESA. Функции VESA SVGA вызываются как подфункции 4Fh прерывания 10h. Номер функции 4Fh должен находится в регистре AH, номер подфункции (номер функции VESA) должен находится в регистре AL Видеорежимы SVGA по стандарту VESA Обобщаем. Видеорежимы бывают текстовые и графические, различаются разрешением экрана и количеством цветов, одновременно выводимых на экран. В текстовом режиме в видеопамяти находятся коды символов и их атрибуты, которые из таблицы символов выводятся на экран монитора. В графическом режиме в видеопамяти находится код цвета каждой точки, отображаемой на экране.
Видеорежимы меняются из программ с помощью вызова функции BIOS. Все функции BIOS вызываются с помощью прерывания 10h. Значения регистров при вызове функции смены видеорежима: AH=0 — номер функции BIOS; AL — номер видеорежима, который нужно включить. Фрагмент программы, в котором устанавливается видеорежим 13h.
Структура видеопамяти в 256-цветных видеорежимах Одни из самых простых в программировании графики режимов 256-цветные. Для VGA это единственный режим 13h, у SVGA несколько таких режимов. Программирование 256-цветных режимов SVGA незначительно отличается от режима 13h. Основное отличие связано с тем, что не вся видеопамять, отображаемая на экране, одновременно доступна, и приходится заполнять видеопамять частями.
Адрес точки для всех 256-цветных режимов вычисляется следующим образом:
Палитра Из 262144 (=218) цветовых оттенков, которые может создать стандартная VGA-карта, на экран выводятся одновременно всего лишь 256 цветов. Почему? VGA адаптер имеет встроенный Цифро-Аналоговый Преобразователь (ЦАП), который содержит 256 регистров цвета. Величина каждого регистра 18 бит. Из этих 18 бит на красный цвет отводится 6 бит, 6 бит — на зеленый цвет и 6 бит — на синий (218=262144). Номер цвета в видеопамяти — это номер регистра цвета, а цвет точки зависит от значения, хранящегося в этом регистре.Выбрать палитру — значит установить нужные значения в регистрах цвета. Для установки одного регистра цвета используют 10h подфункцию 10h функции BIOS. Значения регистров при вызове функции установки регистра цвета следующие:
Программирование графики В основе любой графической функции лежит несколько простых графических функций, называемых графическими примитивами. Такими примитивами являются прорисовка точек, линий, окружностей, а также заполнение областей и перемещение битовых образов.Точка Проще всего записать точку на экран в любом графическом режиме вызвав функцию 0Bh прерывания 10h. Содержание регистров при вызове функции: AH=0Bh, в AL цвет пикселя (если бит 7=1 выполняется операция логического исключающего ИЛИ с цветом экрана), в BH номер страницы, в CX — X координата пикселя, в DX — Y координата пикселя.Если Вы хотите писать напрямую в видеопамять, то например установка точки в режиме 13h сводится к записи кода цвета в видеопамять по адресу, соответствующему адресу точки A=Y*H+X. Ниже приводится программа, рисующая три точки разными цветами в разных местах экрана. Подпрограмма Draw_pixel выводит точку с заданными координатами и с заданным цветом. При вызове данной подпрограммы в регистре BX должна находиться X координата точки, в регистре CL — Y координата, в регистре DL — цвет точки.
Рисование линий на экране Проведение линий подразумевает установку на экране всех точек, принадлежащих отрезку. Сложность при рисовании линии в том, что точки из которых мы ее строим создавали иллюзию прямой. На экране абсолютно точно можно нарисовать только вертикальные, горизонтальные и 1:1 диагональные линии.Если у нас установлен 13h графический режим (320x200x256), то горизонтальную линию рисуют следующими командами
Давайте рассмотрим случай, когда горизонтальная проекция линии длиннее вертикальной и угол наклона линии отрицательный. Например, нарисуем линию направленную из точки (0, 0) в точку (55, 12). Программа должна провести линию в 55 пикселя по горизонтали и 12 пикселей по вертикали. Поскольку наклон линии находится в пределах 55/12=4,5833…, то можно точно сказать что линия будет состоять из чередующихся рядов точек (прогонов) из 4 (минимальный прогон) и 5 точек (максимальный прогон). Как разместить прогоны с минимальной и максимальной длиной? Остаток деления 55 на 12 равен 7, равномерно раскидаем эти дополнительные 7 точек. На каждый шаг по оси Y мы ставим, по меньшей мере, 4 пикселя вдоль оси X. После этого нам нужно решить, ставить ли пятый или переходить на следующую координату по оси Y. Если мы подсчитаем, какую ошибку отклонения мы накопили, и если ошибка накопления превышает 0,5 пикселя — тогда нам нужен в отрезке дополнительный пиксель. Длина минимального отрезка по оси X на один шаг по оси Y составляет XDelta/YDelta=4,5833… Обозначим XDelta%YDelta получение остатка от деления XDelta на YDelta, ошибка накопления в каждом прогоне составляет (XDelta%YDelta)/YDelta=7/12=0,5833… дополнительный пиксел потребуется если (XDelta%YDelta)/YDelta — ½ > 0 (XDelta%YDelta) — YDelta /2 > 0 (XDelta%YDelta)х2 — YDelta > 0 то есть, на каждом шаге по оси Y накапливается ошибка (XDelta %YDelta)x2. Если ошибка достигнет одного пикселя или больше, то мы добавим к прогону дополнительный пиксель и вычтем из значения ошибки YDelta*2 Теперь попробуем нарисовать линию — на каждый шаг по оси Y будем ставить по 4 или 5 точек по оси X. Если соединить центры наших прогонов, то окажется, что наша линия смещена на 2 или 3 точки от идеальной линии. Чтобы точки максимально близко ложились к идеальной линии применяется балансировка прогонов. Для этого берут один максимальный прогон и распределяют его равномерно между первым и последним отрезком, чтобы концы линии стали симметричными. Ниже приводится листинг программы реализующее построение линии по алгоритму Брезенхейма с переменной длиной отрезков.
Рисование окружности Построить окружность несложно. У окружности координаты любой точки относительно ее центра вычисляются из соотношения R2=X2+Y2, где R радиус окружности. С точки зрения программирования достаточно нарисовать 1/8 часть окружности, а симметрия закончит дело. В языках программирования высокого уровня существует специальная функция CIRCLE, которая строит окружность либо, вычисляя синус, либо, двигаясь например по оси X вычисляет на каждом шаге координату Y по формуле и так в цикле. Вы выводите N точек, где N вычисляется из значения
12
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
|||||||||||||||||||||||||||||||||||||
| 30.05.2013, 12:08 [ТС] | |||||||||||||||||||||||||||||||||||||
|
Вывод монохромного рисунка на экран в режиме от 0Dh до 12h Программа приведенная ниже будет работать без изменений в следующих графических режимах приведенных в таблице
Вывод монохромного рисунка на экран в режиме 13h (320х200х256 цветов) Исходный текст, СОМ-файл и картинка здесь
Вывод монохромного рисунка на экран в VESA-режиме 640x480x256 Исходный текст, СОМ-файл и картинка здесь
3
|
|||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 30.05.2013, 12:15 [ТС] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
описание ключей ml.exe ml.exe - это транслятор языка Macro Assembler фирмы MicroSoft. Главная задача этой программы: из исходника перевести все команды Ассемблера, набранные текстом в коды машинных команд. Сам ml.exe не создает готовую программу под конкретную операционную систему и её формат. Он делает промежуточный, так называемый объектный файл (с расширением obj). Но после ml.exe вызваем линковщик. Так мы получим готовую программу. Как и большинство трансляторов разных языков программирования, ml.exe - это программа с интерфейсом командной строки. Здесь мышка не поможет, нет смысла пытаться "открывать" компилятор из проводника без указания в командной строке хотя бы файла исходника. Вид строки:
/link - параметр, с которым указываются ключи_линковщика (см. таблицы ключей). список_файлов - имена исходных файлов, которые будут транслироваться. ключи - параметры компиляции, перечисленные ниже (ключи чувствительны к регистру). Параметры, которые вы используете постоянно, можно добавить в переменную окружения ML. Транслятор будет учитывать эти опции всегда. Форматы трансляции Главная характеристика трансляции - это тип выходного объектного файла.ML.EXE умеет создавать только два вида obj-файлов.
4
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
1127 / 261 / 9
Регистрация: 11.06.2010
Сообщений: 1,049
|
|||||||||||
| 30.05.2013, 15:57 | |||||||||||
|
FPU. Округление. Порой бывают такие случаи, когда надо вытащить целую часть дроби. В этом случаи все прибегают к помощи такой связки, как
Например, у меня был случай, когда при делении надо было от частного оставлять только целую часть, но связка, упомянутая выше, в случае с частным 0.501 выдавала результатом 1, а не 0. Когда такое происходит, помогает вот такая связка:
После применения данной инструкции всё заработало так, как надо. Вот так.
4
|
|||||||||||
|
608 / 406 / 8
Регистрация: 26.04.2012
Сообщений: 2,065
|
||||||
| 31.05.2013, 07:34 | ||||||
|
процедура пищания динамиком под досом.
2
|
||||||
|
608 / 406 / 8
Регистрация: 26.04.2012
Сообщений: 2,065
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 03.06.2013, 08:35 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Длинные числа Стандартные типы данных позволяют хранить не очень длинные числа. в регистре еах максимум может поместиться число 4294967295. Для некоторых целей оно очень мало. Чтобы оперировать числами вида 10^Nпри N>40 нужно изобретать свой тип данных. Один из вариантов – массив.Предположим, необходимо вычислить 30!: 30!=2*(104)8+6525*(104)7+2748*(104)6+8121*(104)5+9105*(104)4+8636*(104)3+3084*(104)2+8000*(104)1+0*(104)0
ВВОД ЧИСЛА Опишем данные:
Алгоритм протаскивания старшей цифры из A[i] в младшую цифру A[i+1]:
Готовая процедура:
Казалось бы, нет проблем — выводи число за числом. Однако в силу выбранного нами представления числа необходимо всегда помнить, что в каждом элементе массива хранится не последовательность цифр числа, а значение числа, записанного этими цифрами. Пусть в элементах массива хранятся четырехзначные числа. И есть число, например, 128400583274. При выводе нам необходимо вывести не 58, а 0058, иначе будет потеря цифр. Итак, нули также необходимо выводить. Процедура вывода имеет вид:
содрано и переведено на асм (Окулов С.М. "Программирование в алгоритмах" )
4
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
608 / 406 / 8
Регистрация: 26.04.2012
Сообщений: 2,065
|
|||||||||||||||||||||||||||||||||||||||||
| 13.06.2013, 09:58 | |||||||||||||||||||||||||||||||||||||||||
|
Процедура сложения длинных чисел:
программа вводит два числа, складывает их и выводит результат на экран.
умножение длинного числа на короткое Под коротким числом понимается число меньшее основания системы счисления.
для особо скептичных: факториал 9999: Кликните здесь для просмотра всего текста
подсчет факториала любого числа меньшего 1000000000 (проверял только на 50000)
5
|
|||||||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 25.06.2013, 05:06 [ТС] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
несколько способов создания файла в DOS
3
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
||||||
| 25.06.2013, 13:16 [ТС] | ||||||
|
Выдираю из io.sys (можно из ntvdm) кусок 21h прерывания, отвечающего за создание файла. Упрощаю и выкидываю "всё лишнее", в результате получаю СОМ-файл, создающий файл на диске и не использующий прерывания.
Кликните здесь для просмотра всего текста
2
|
||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 25.06.2013, 13:20 [ТС] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Поиск файлов и вывод содержимого каталога на экран. DOS Поиск файлов в DOS реализуется, как правило, через функции 4Eh и 4Fh прерывания 21h. Эти функции позволяют обнаруживать в заданном каталоге или на заданном устройстве файлы с именами, соответствующими заданному образцу.В DOS существует возможность обращаться к группе файлов, задав так называемую маску имени. При задании маски используются обозначения: ? - любой символ, * - любая группа символов. Например:
Приведем формат вызова функций поиска:
Приведем пример программы, ищущей и отображающей на экране имена всех файлов с расширением .СОМ в текущем каталоге:
Поиск файлов и вывод содержимого каталога на экран. Вывод списка файлов и подкаталогов из указанного каталога.Используем системную команду DIR
их действия введите в команде те же ключи с префиксом "-", например: /-W.
Поиск файлов и вывод содержимого каталога на экран. Windows Пример из книги Пирогова "Ассемблер для Windows". Программа осуществляет поиск в указанном или текущем каталоге. Поиск проходит по дереву каталогов, начиная с заданного
6
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
|
| 25.06.2013, 13:20 [ТС] | |
|
ОСНОВНЫЕ ПРАВИЛА НАПИСАНИЯ ПРОГРАММ НА ЯЗЫКЕ АССЕМБЛЕРА Данные правила относятся не только к программированию на языке ассемблера, но и к программированию на других языках. Может быть, их трудно понять, не имея навыка в программировании, но «незнание основ не освобождает от ответственности».Начинайте с комментариев Начните с написания инструкции для пользователя — для чего создается и каковы возможности вашей программы. А теперь немного усложните вашу инструкцию по применению вашей программы, подразумевая под «пользователем» программиста, использующего написанный вами код — зачастую этим программистом-исследователем будете вы сами. Акт записи на обычном языке описания того, что делает программа и что делает каждая функция в программе, является критическим шагом в мыслительном процессе. Хорошо построенное, грамматически правильное предложение — признак ясного мышления. Если вы не можете это записать, то велика вероятность того, что вы не полностью продумали задачу или метод ее решения. Плохая грамматика и построение предложения являются также показателем поверхностного мышления. Поэтому первый шаг в написании любой программы — записать, что именно и как делает программа. Итак, комментарии для вашей программы уже готовы. Теперь возьмите ваше описание по использованию и добавьте вслед за каждым абзацем блоки кода, реализующие функции, описанные в этом абзаце. Оправдание: «У меня не было времени, чтобы добавить комментарии» на самом деле означает — «Я писал этот код без проекта системы и у меня нет времени воспроизвести его». Если создатель программы не может воспроизвести идеи, воплощенные в программный проект, то кто же тогда сможет? Работа программиста состоит из двух частей: разработать приложение для пользователя и сделать возможным дальнейшее сопровождение программы. Единственный способ решить вторую часть задачи — комментировать код. Причем комментарии должны описывать не только, что делает код, но и предположения, принятый подход и причины, по которым вы выбрали именно его. Кроме того, необходимо, чтобы комментарии также соответствовали коду. Пишите код так, словно тот, кто будет заниматься его поддержкой, — опасный психопат, знающий где вы живете. Хотя вы можете считать, что ваш код полностью очевиден и может служить примером ясности, без правильных комментариев понять его постороннему достаточно трудно. Парадокс заключается в том, что спустя неделю вы сами можете оказаться в роли этого постороннего. Старайтесь использовать следующий подход при написании комментариев:
Читайте код Все писатели — это читатели. Вы учитесь, когда смотрите, что делают другие писатели. Я настоятельно рекомендую, чтобы, как минимум, члены группы программирования читали код друг у друга. Читатель может найти ошибки, которые вы не увидели, и подать мысль, как улучшить код. Для вас лучше присесть с коллегой и просто разобрать код строка за строкой, объясняя, что и как делается, получить какую-то обратную связь и совет. Для того чтобы подобное упражнение принесло пользу, автор кода не должен делать никаких предварительных пояснений. Читатель должен быть способен понимать код в процессе чтения. Если вам пришлось объяснять что-то вашему читателю, то это значит, что ваше объяснение должно быть в коде в качестве комментария. Добавьте этот комментарий, как только Вы его произнесли; не откладывайте этого до окончания просмотра.Разлагайте сложные проблемы на задачи меньшего размера На самом деле это также и правило литературного стиля. Если очень трудно объяснить точку зрения за один раз, то разбейте изложение на меньшие части и по очереди объясняйте каждую. То же самое назначение у глав в книге и параграфов в главе.Используйте язык полностью Некоторые программисты считают одним из недостатков языка ассемблера большее, по сравнению с языками высокого уровня, количество команд, но, по-моему, это одно из достоинств языка ассемблера.Проблема должна быть хорошо продумана перед тем, как она сможет быть решена Это относится не только к программированию на языке ассемблера.![]() Отредактируйте свой код. Программа должна писаться не менее двух раз Раньше, когда вы изучали в школе литературу, вам никогда не приходило в голову сдавать черновик письменного задания, если Вы, конечно, рассчитывали на оценку выше тройки. Тем не менее, многие компьютерные программы являются просто черновиками и содержат столько же ошибок, сколько и черновики ваших сочинений. Хороший код программы должен быть сначала написан, а затем отредактирован в целях улучшения (под «редактированием» имеется в виду «исправление»). Редактирование, как правило, приводит к сокращению кода, а небольшие программы выполняются быстрее.Оптимизация программ на языке ассемблера Итак ваша программа заработала, а теперь постарайтесь переделать ее так, чтобы она стала максимально компактной и в тоже время максимально быстродействующей.Такая оптимизация достигается в три этапа:
Для написания этого топика были использованы статьи из следующих книг
4
|
|
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
|||||||||||||||||||||||||||||||
| 25.06.2013, 15:13 [ТС] | |||||||||||||||||||||||||||||||
|
Графика DOS. Двигаемся в тоннеле Tunnel - 256 bytes01-11-2002 256 bytes intro: realtime rendering, software rendering, MsDos, x86 real mode, assembler, demoscene Автор Iсigo Quñlez, взято здесь и немного доработано
Графика DOS. Еще один тоннель Здесь используется bmp-файл в качестве подложки, поэтому можно организовать путешествие сквозь водоворот, ледяную или каменную пещеру, кишечник и т.д. всё зависит от степени вашей испорченности ![]()
Графика DOS. Цветок В общем, палитру выставляйте как хотите, главное, чтобы переходы между цветами были плавными.Для хранения изображения "цветка", заведем массив flower, размером 640x400. Нарисуем в нем наш "цветок". Формулу для рисования "цветка" можно найти в любом нормальном учебнике по "вышке". Для всех тех кто уже успел забыть этот кошмар или у кого он еще не начался, так уж быть, вот процедура инициализации массива:
![]() Психоделическая составляющая эффекта Теперь определимся сколько нам потребуется "цветков" на экране, мне хватило трех, при большем количестве "цветков" на экране довольно трудно становиться понять, что твориться на экране. Правда если хочешь "словить глюков" - поставь цветков так 5 - 7 и смотри на экран минут этак пять, глюков может и не словишь, но глаза заболят обязательно.![]() Но перед тем, как мы начнем наслаждаться резью в глазах надо еще сделать кое-что. Возьмем какую-нибудь точку в массиве flower, причем не любую, а такую, чтобы X координата лежала в промежутке от 0 до 320, а Y координата в промежутке от 0 до 200. Начиная с этой точки скопируем кусок массива, размером 320x200, на экран. Получим изображение куска "цветка". Если теперь сдвигать начальную точку, в каждом новом кадре, на новое место, то получим изображение перемещающегося цветка. Теперь допустим, что начальных точек две, и на экран копируется не один кусок "цветка", а два. Причем накладывается они друг на друга обычным сложением цветов. В результате получим требуемый эффект. Вот. Реализуется это примерно так:
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 1106 байт до 885 байт
2
|
|||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 25.06.2013, 15:13 [ТС] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Скрываем от посторонних глаз наиболее критичные части программ Шифрование Для затруднения изучения алгоритма программы со стороны постороннего человека часто используется шифрование кода. При использовании дизассемблера исследователь не сможет получить адекватный листинг
Чрезвычайно усилить эффект кодирования позволяет техника, предусматривающая постоянное изменение алгоритма шифровки/дешифровки. Так, например, можно использовать не только прибавление/вычитание единицы, но и других чисел, а также умножения/деления, логические операции над байтами и так далее. Сама реализация дешифрующего фрагмента также может быть различной. Меняя регистры для хранения служебных данных, способы записи арифметических и логически операций, способы организации цикла и прочее, можно получить сколько угодно различных вариантов, исполняющих один и тот же алгоритм расшифровки. Прячем «лишние» ассемблерные команды в обычном коде. Данный метод полезен для усложнения дизассемблирования кода, особенно, если генерацию «скрытых» команд автоматизировать. Взглянем на следующий код, в котором скрыто намного больше команд, чем видно на первый взгляд:
Давайте поставим break-point на последний оператор NOP, запустим и посмотрим результат.
Посмотрим на код повнимательнее. Первая команда помещает в регистр EAX значение 1EBC031, вторая команда помещает в EBX значение 90DB3190. CMP сравнивает два регистра. JNE делает переход, если значения регистров не совпадают. А вот тут самое интересное — переход делается не в начало первой команды, а на второй байт первой команды. Выполнился короткий переход, и что же мы видим? Две спрятанные команды — XOR EAX,EAX выполняет обнуление регистра EAX. И короткий переход, который передает управление внутрь другого MOV-а.
Дело в том, что в регистр EAX записано не простое число, а машинный код команды. Из-за таких «накладок» код практически не возможно представить на языке ассемблера. Ассемблерные команды имеют разный размер. Команда «MOV EAX,1EBC031» занимает 5 байт, когда как команда XOR EAX,EAX — 2 байта. С помощью команд бинарного сдвига и, используя полу-регистры AL и AH, можно такими «прыжками» набрать обработку и ввод целого регистра EAX. Хочу отметить, что при вставке машинных команд, как параметр MOV, нужно менять последовательность байтов, так например команда 31CO(XOR EAX,EAX), помещенная в команду MOV будет выглядеть как «MOV EAX,C031». Данный метод применяется во многих системах защиты программного обеспечения, затрудняя поиски перехода. Взято здесь
3
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ушел с форума
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 25.06.2013, 15:13 [ТС] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Графика DOS. Эффект кипящей лавы. Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 369 байт до 234 байт
________________________________________ Графика DOS. Блюр-эффект. Довольно простой эффект, как раз для начинающих. К тому же, этот эффект лежит в основе целого семейства эффектов, и знать его необходимо!Blur, в переводе с английского: неясные очертания, размытое пятно. Допустим, у нас есть некое изображение размером 320x200x8bit, в буфере экрана screen. Создаем дополнительный буфер экрана, buffer. Цвет точки с координатами X,Y в массиве buffer будет рассчитываться по формуле: цвет_точки = среднее арифметическое от значений цвета точек, окружающих точку с координатами X,Y в массиве screen. После применения данной формулы ко всем точкам массива buffer, в этом массиве будет находиться сBlur-енное изображение. Некоторые замечания:
Пример:
Как от этого избавиться? В true блюре - никак. Но есть вариант блюра, в котором этот эффект не появляется:
Преимущества:
Искажение геометрических соотношений изображения. Обычно такой вариант Blur`a используется для улучшения качества картинки, на различных стадиях вывода изображения. Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 498 байт до 408 байт.
Графика DOS. Anis-эффект. Как скролить (вправо-влево, вверх-вниз), надеюсь, вы знаете. Что такое полярные координаты, наверно то же. Так вот этот эффект основан на скролинге, но в полярных координатах.Соответственно скролинг вдоль координаты r - выглядит как втягивание-вытягивание, а изменение угла alpha - как повороты по часовой стрелке или против. Вот и вся теория. Теперь начнется практика. Пусть центр нашего anis`a находиться в центре экрана. Тектуру которую мы хотим извращать, возьмем размером 256x256. Основное требование к этой текстуре - быть бесшовной, чтобы удобно было скроллить. Посмотрите на мою текстуру, чтобы понять о чем речь: в не имеющих названия единицах, которых в круге 256. Теперь сопоставим углу alpha - координату x в нашей текстуре, а координате r - y нашей текстуры. Теперь, что бы втягивать-вытягивать и вращать текстуру, достаточно изменить начальные смещения по x и y в нашей текстуры. Реализуем наши соображения в алгоритме, он разделится на две части: 1) Инициализация. Заведем два массива texture и anis. В первом храним нашу текстурку, а во втором (типа unsigned short, размер такой же как у экрана) будем хранить смещения текстуры. Далее в цикле проходим по всем точкам массива anis, преобразуем декартовы координаты текущей точки в полярные, по этим координатам вычисляем смещение от начала массива texture, и заносим его в текущую ячейку массива anis:
Для каждого выводимого кадра решаем насколько нам надо повернуть, втянуть-вытянуть нашу текстуру, то есть находим сдвиг по координате r и углу alpha. В цикле проходимся по массиву anis, считываем из него смещение, прибавляем вычисленное ранее сдвиг, и по этому смещению считываем пиксель из массива texture. Записываем его по текущим координатам в буфер экрана:
Графика DOS. Светлячки. На самом деле этот эффект называется blow ("раздувание").Установим палитру так: 0 Наши светлячки - обыкновенные спрайты, точнее, один спрайт размером 256x256. Назовем массив, в котором будет храниться этот спрайт, blow. Формула по которой мы будем рисовать выглядит так: color=max_radius2/(radius2+max_radius), где max_radius - это требуемый радиус огонька, radius - радиус текущей точки. Сущность этой формулы: смягченная обратная зависимость яркости от радиуса, квадраты радиусов используются из-за того, что вычислить квадрат радиуса текущей точки намного легче, чем просто радиус (не надо извлекать корень). Максимальное значение цвета ограниченно числом 63. Процедура инициализации будет выглядеть так:
Тогда процедура вывода спрайта будет выглядеть так:
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 912 байт до 740 байт
Графика DOS. Эффект пламя. В сущности, пламя, это тот же эффект Blur, изменений совсем немного.Допустим, мы хотим изобразить самый простой вариант: Пламя поднимается снизу экрана и постепенно гаснет. Наша формула true-blur`а изменится совсем чуть-чуть:
Да, и самое главное, чтобы пламя "горело", его надо постоянно "подпитывать": в самую нижнюю строчку в начале каждого кадра записывать пиксели со случайным цветом. Добавится вот такая строка:
Опять же, для ускорения можно использовать вариант блюра с четырьмя усредняемыми пикселями, немного его изменив:
3
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 25.06.2013, 15:13 | |
|
Помогаю со студенческими работами здесь
20
Видеоуроки по Ассемблеру MASM/TASM (для DOS) на русском языке MASM, TASM, FASM: что выбрать для программирования в ядре Есть ли компиляторы Tasm или Masm для 64-разрядных систем Подскажите ссылки на FAQ для разделов Assembler, MASM, TASM Assembler Переделать из TASM в masm Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
||||
|
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта
Симптом:
После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
|
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
|
Новый ноутбук
volvo 07.12.2025
Всем привет.
По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне:
Ryzen 5 7533HS
64 Gb DDR5
1Tb NVMe
16" Full HD Display
Win11 Pro
|
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
|
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
|
|
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов
На странице:
https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/
нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
|
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов.
. . .
|
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
|
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
|
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут.
В век Веб все очень привыкли к дизайну Single-Page-Application .
Быстренько разберем подход "на фреймах".
Мы делаем одну. . .
|