Последние записи
- Windows 10 сменить администратора
- Рандомное слайдшоу
- Событие для произвольной области внутри TImage
- Удаление папки с файлами
- Распечатка файла
- Преобразовать массив байт в вещественное число (single)
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
29th
Сен
Разработчик интерфейс пользователи. Часть 2
По своей работе я оказался в роли разработчика программы, работающей с базой данных. Самое основное в этом процессе — быстрый ввод информации в программу. Форма для ввода данных была создана еще до меня, и пользователи не один год работали с нею. Однажды, по стечению обстоятельств, мне пришлось сменить профиль и перейти из роли разработчика в роль пользователя.
Продолжение. Начало цикла смотрите в предыдущем номере журнала…
Александр Демьяненко
by Grenles GRENLES@yandex.ru
Лишние мелкие движения
Одно дело, когда ты пишешь код и в течение нескольких минут его тестируешь, и совсем другое, когда ты работаешь с ним. Повторюсь и поставлю акцент на слово «работать». Эти процессы весьма разнятся. Когда пишешь код — ты решаешь задачи реализации. Когда работаешь с исполняемым кодом — решаешь задачи эксплуатации. И вот при эксплуатации, как всегда и бывает, возникает множество вопросов и нюансов — тех самых, которые так не любят доделывать разработчики, считая этот процесс бесконечным и ненужным. Число исписанных страниц с замечаниями в моем блокноте каждый день росло. Не скажу, что я исправил все ошибки и неточности, — до сих пор мой список еще не исчерпан, и тому есть разные причины. Однако эту ошибку — на мой взгляд, неудобную, — я исправил. Вполне допускаю, что найдутся пользователи, которых устраивает старый вариант реализации ввода данных, но мне кажется новый вариант лучшим.
****** Самое главное для хорошего разработчика — уметь признать и исправить собственные ошибки. Впрочем, это умение не помешает любому человеку…
Итак, вернусь к собственному примеру (см. Рис. 1):
Рис. 1. Часть формы ввода информации в базу данных
Обычный процесс ввода информации идет так: ввод даты, лицевого счета, данных — и дальше все по кругу. Где же неудобство? На первый взгляд, его тут вовсе нет. Секрет кроется в исходных данных. Дело в том, что дата изменяется только от листа к листу с данными; в пределах одного листа дата неизменна. Вот и выходит, что операция ввода даты в пределах одного листа — лишняя. Если на листе 30 записей, то надо выполнить, условно говоря, 3х30 действий. Выкинув цикличный ввод даты, число действий можно сократить до 2х20+1 (первоначальный ввод даты). Есть два решения этой проблемы. Первый — каждый раз оставлять дату неизменной; тогда при получении фокуса ввода в поле даты пользователь должен нажимать какую-либо клавишу, чтобы пропустить ввод даты. Это чуть лучше, но не избавляет от лишних действий. Второй способ — единожды дать пользователю ввести дату, а потом автоматически переключать фокус ввода только между полями лицевого счета и данных. При необходимости сменить дату пользователь может воспользоваться или мышью,
или специальным сочетанием клавиш.
Кажется, это все мелочи. Но наша жизнь как раз и складывается из разных мелочей, а если их предусмотреть и учесть, то пользователю будет удобно и приятно, да и ваш статус в их глазах заметно вырастет.
Проблема безусловной интерпретации данных
Всем известен Microsoft Excel, входящий в состав Microsoft Office. Ни для кого не является секретом, что эта программа стала де-факто основным средством ведения учета и документооборота в различных компаниях и фирмах. Укажу на одно неудобство, заложенное в эту программу, которое меня раздражает тем, что никто не удосужился спросить, а надо ли мне это. Не буду стопроцентно утверждать, но та же проблема есть и в Open Office, который является бесплатной альтернативой платному продукту от Microsoft.
У компании Microsoft есть одно решение в части интерпретации вводимых данных. Думаю, многие сталкивались с ситуацией, когда при вводе данных в ячейку вида «9/12» или «9-12» программа Excel автоматически переводит эти данные в формат даты (см. Рис. 2). Раздражает отсутствие вариантов: перевод происходит автоматически (о том, как это исключить, напишу ниже). За меня кто-то решил, что надо поступать только так, а не иначе. Честно скажу: я пытался найти опцию в настройках, как-то разрешающую это действие, и не нашел.
Рис. 2. Интерпретация данных MS Excel до и после ввода
В зависимости от настроек предпочтений результат отображения даты может быть различным, как это и показано на Рис. 2. Для случаев 1, 2, 3 явно видно, что программа интерпретировала данные как дату. При высоком темпе работы, усталости оператора, большом объеме данных эту «ошибку» можно запросто пропустить. В дальнейшем она может вызвать совершенно непредсказуемые неприятности при обработке данных, например, потерю точности, если предположить, что данные мог вводить один человек, а использовать их — другой. У второго человека может не быть информации, что «9 декабря» — это не дата, а «9/12». В случае 4 на Рис. 2 (скажу сразу: я сделал его искусственно, но такой вариант возможен), кажется, все в том же виде, как и введено, и такую ситуацию тем более можно пропустить. На самом же деле «9.12» — это не число, а дата «9.12.2010», просто в этом формате на экране год не отображается.
Допустим, что пользователь заметил ошибку и решил ее исправить. Пользователь у нас все-таки человек умный и не один год работает за компьютером, то есть не «чайник» и не совсем уж глуп — он некий «середнячок», имеющий навыки и опыт. Поэтому он решил сделать обратную операцию — «вернуть формат». Что он увидит, думаю, опытные пользователи и программисты догадаются: вовсе не то, что хотел увидеть в результате (см. Рис. 3):
Рис. 3. Итог попытки «вернуть формат назад»
Как видно, пользователь получил странное число «40521», а не «9/12» как бы ему того ни хотелось. Почему? Программисты знают ответ: а потому, что кто-то за нас при разработке программы решил, что это число дней, прошедшее с даты «01.01.1900». С точки зрения такой логики, программа сделала все верно и сделала из даты — число. Но только тут кроется еще одно умолчание, которое и ошибкой не назовешь, но в данном случае — это ошибка, так как ожидания пользователя естественным образом обманывает.
Самое интересное во всей этой ситуации с превращениями, что «9/12» и не число, и не дата, а ТЕКСТ (!). В данном случае есть как минимум два выхода из этой ситуации: либо задавать пользователю вопрос: «Интерпретировать данный текст как дату?», либо где-то в настройках программы сделать «флажок», разрешающий эту операцию.
Ручное решение данной проблемы таково: сделать ячейки, куда предполагается ввод данных подобного вида заранее «текстового» формата. Как известно, «текстовый» формат Excel никак не интерпретирует. Сложность может возникнуть в том случае, когда заранее неизвестно, в какую ячейку могут быть введены данные подобного вида. Если сделать все ячейки «тестом», то это плохо для данных формата «числовой», так как ни одна математическая операция их не воспримет как число, а выдаст ошибку.
Казалось бы, одно удобство, а столько сложностей! Не открою никакого секрета и в этот раз, если скажу, что большинство программ в качестве промежуточного формата для обмена данными использует формат файла MS Excel. Разработчики, зная такую особенность этой программы, заранее делают все ячейки текстового вида (правда, это делают грамотные и уважающие пользователя разработчики, некоторые забывают) и уже потом выгружают данные в файл. Точность данных сохраняется, но удобства не прибавляется. И к тому же возникает задача преобразовать формат ячейки «текстовый» в формат ячейки «числовой» для данных, являющихся числами, чтобы дать возможность пользователю выполнять математические операции над числами. В итоге безусловное «удобство» оказывается источником множества лишних действий.
Интерпретация как таковая
Обобщу проблему интерпретации, но перед этим скажу, как она касается интерфейса. Известно, что человек не менее 80% информации воспринимает с помощью зрения. Таким образом, мы сначала видим что-то, а потом неосознанно запускается процесс сопоставления увиденного с чем-то ранее известным. В случае, когда подсознательно сопоставить не получается, запускается процесс осознанного сопоставления, или, иначе говоря, — логической интерпретации данных. Значит, то, что нам покажут и что мы увидим, весьма важно, а это как раз и есть интерфейс.
Возьмем текст «14-12-10». Что это такое? Мне кажется, большинство ответит: «это дата 14 декабря 2010 года». А почему? Например, потому, что выше я вел речь о дате и в нашей кратковременной памяти остался контекст, в котором ключевым словом было «дата». А так как другого контекста нет, то мозг его автоматически распространил на новые увиденные данные.
Итак, воспринимаемая информация сильно зависит от того, в контексте какой информации мы ее узнаем. Но ведь эти цифры могут являться номером телефона, кодом для сейфа, частью ряда убывающей арифметической последовательности. Интерпретаций можно придумать множество.
Вернемся к обсуждению примера с датой. Я немного подкорректирую цифры, чтобы было «удобно» поиграть ими: заменю первую «1» на «0». Итак, имеем дату «04-12-10». Применительно к компьютерной технике задам другой вопрос: «Что это за дата?». На первый взгляд, это «4 декабря 2010». Но ведь может быть и «10 декабря 2004 года». Ответ зависит от того, как в программе заложена интерпретация входных данных. Есть несколько нотаций даты, при этом «число-месяц-год» могут меняться местами как угодно. Мало того, если поглядеть представления, какие нам предлагают различные программы, то задача интерпретации данных без контекста вообще не решаема. Приведу всего лишь два формата, в котором все представлено двумя числами.
- ДД.ММ.ГГ
- ГГ.ДД.ММ
Может быть «день-месяц-год», а может быть и «год-день-месяц». Хотя в большинстве случаев принята нотация «день-месяц-год», возможны разные варианты.
Пользователь привык к тому, к чему его когда-то «приучили», и не обязательно вы. Вас тоже к чему-то когда-то кто-то приучил, да еще так, что некоторые вещи вы считаете безусловными и сами собой разумеющимися.
Как дополнительную информацию полностью процитирую источник [12]*******
******* «Приведу реальный пример, как рекламный бюджет выкидывается псу под хвост (нам этот случай рассказывали на семинаре московские рекламисты). Крупная компания-производитель зубной пасты решила выйти на рынок в арабские страны и дала задание агентству найти самый эффективный рекламный ход из уже запущенных, чтобы затраты были по минимуму. Агентство выдало грамотное обоснование: по всей Европе стоят рекламные щиты, на которых нет ни одного слова. Значит, ничего переделывать не надо, а надо просто заказать их побольше и поставить в арабских странах. Компания идею одобрила. На эти цели было выделено около миллиона долларов. Что из себя представлял рекламный щит? Всего три фотографии: на первой молодой парень улыбается желтыми зубами, на второй — чистит зубы, на третьей — улыбка белоснежная. И все это на фоне рекламируемой зубной пасты. Установили щиты в арабских странах. Продаж — ноль! Заказали исследование компании по мониторингу — почему нет продаж. Ответ был быстр: рекламные щиты не возымели своего
действия потому, что в арабских странах читают справа налево…»
Думаю, смысл всего сказанного достаточно прозрачен.
******* Приучая пользователя к чему-либо, подумайте, что он сам об этом думает.
Стандартные сообщения об ошибках. Это…
Прошли те времена, когда каждый придумывал свои форматы, методы сопряжения, способы обмена и решал вопросы совместимости своих данных с данными других коллег. Все эти сложности решила стандартизация.
Стандартизация — это способ согласованного взаимодействия разработчиков между собой. В большинстве случае это очень хорошо. Но, если задуматься, это же одновременно и источник различных проблем. Почти всегда и все отступают и отклоняются от стандартов в угоду различным причинам. И, как часто это бывает, о том, что было отступление от стандарта, либо умалчивают, либо забывают сообщить (что, по сути, одно и то же).
Долго думал, какой же пример привести, чтобы он был понятным и наглядным. Были соображения сравнить две версии MS Office 2003 и MS Office 2007 в плане значительной смены интерфейса и появления новых элементов. В определенной степени этот пример подходит под заданную тему «стандарты», но все же не нагляден. А кто сказал, что отображение элементов интерфейса в одной программе есть стандарт и остается неизменным много лет подряд? Пожалуй, так же подумали разработчики MS Office — и кардинально изменили интерфейс своих приложений.
Были мысли поговорить о цветовых схемах различных программ, но это тоже не в тему. Во-первых, если разобраться, то практически любой уважающий себя разработчик дает возможность пользователю сменить «скин» («оболочку», внешний вид) своей программы, в том числе шрифты и цвета отображаемых элементов. Собственно говоря, идея была раскритиковать программы, где это сделать невозможно, а подбор и сочетание цветов неприятны при использовании программы, но таковых сейчас очень мало или почти нет. Даже на некоторых «продвинутых» сайтах и то есть возможность смены оболочки.
В итоге решил написать вот про что. Как известно, предугадать и исправить все возможные ошибки нереально. В современных языках программирования есть различные средства для выхода из ошибочных ситуаций, которые не были предусмотрены разработчиком. Одно из них — обработка исключений. Это хорошее средство, шаг навстречу пользователю. Даже если разработчик забыл где-то вставить отработку возможной ошибки, за него это сделает операционная система. Но в данном случае речь немного о другом. Точнее сказать, взгляд под другим углом зрения. Обработчик ошибочной ситуации выводит на экран сообщение об ошибке, в котором указывается какая-то информация об ошибке. То есть отработка ошибочной ситуации «вывод информации» — это уже стандарт. Сложность в другом: каждый делает это по-своему!
Возьмем, например, сеть Интернет. Все пользователи рано или поздно сталкиваются с сообщением об ошибке 404, которое появляется в том случае, когда запрашиваемая страница не найдена. Это логично: когда нет запрашиваемого источника — надо выводить об этом информацию. В Интернете есть даже целый сайт, посвященный тому, что на нем собраны все возможные варианты ответов для пользователя с сообщением об этой ошибке. Пока все верно и правильно.
Теперь рассмотрим программу (даже неважно, какую именно), в которой может возникнуть любая ошибка. Как известно, в исключительной ситуации пользователю будет выдано сообщение об ошибке. В лучшем случае будет указан номер ошибки и поясняющий текст, в худшем — просто номер ошибки, а там — догадывайся сам, как хочешь. Пока все вписывается в стандарт — пользователь информируется.
Ошибка в этом случае совершенно другая, и заключается она как раз в том, что пользователь информируется об ошибке. Это стандартно и признак хорошего тона — информировать об ошибке, но, давайте перейдем на сторону пользователя. Мне сообщили об ошибке, написали ее номер и текст. И что мне делать дальше? Закрыть сообщение и аварийно завершить программу. При повторном выполнении тех же действий снова случится та же ошибка. А мне, как пользователю, необходимо выполнить некоторые действия с этой программой, но разработчик не дает мне такой возможности.
С моей, пользовательской, точки зрения, сообщение об ошибке означает то, что мне в красивой форме говорится о том, что я глуп. А если подумать еще чуть-чуть, мне это ни о чем не говорит, кроме того, что разработчик не захотел позаботиться обо мне. Какое мне дело, что произошло деление на ноль, или переполнение стека, или страница не найдена? Мне нужен конечный результат, которого я хочу достичь, используя предлагаемые разработчиком средства, а сообщения об ошибке мне только мешают, обрывая мой путь к достижению цели.
Если задуматься над этой задачей, то решение может быть таким. Конечно же, есть ошибки, которые просто неизбежны, фатальны, и ничего с ними нельзя сделать. Отказ оборудования, отсутствие страницы на сайте, сбой локальной сети и прочее — эти причины предвидеть сложно. Но нужно постараться сделать так, чтобы у программного продукта было предусмотрено свое решение этих проблем, помимо стандартного сообщения об ошибке.
При отсутствии страницы на сайте можно посоветовать пользователю перейти на другие страницы или предложить другой способ поиска нужной информации. А если серьезно, то на сайтах не должно быть устаревших и отсутствующих страниц.
Если это программа, то, например, обрыв сетевого соединения не должен вызывать потерю данных, особенно если идет закачка из Сети большого по объему файла. Кстати, до сих пор этой болезнью болеет Internet Explorer — при обрыве соединения или таймауте (времени, в течение которого источник не отвечает), при повторной попытке файл начинает закачиваться сначала, а не с места обрыва. Наверное, в том числе и это стало одной из причин появления программ-загрузчиков информации из Сети. В FireFox такой проблемы нет, браузер при обрыве соединения предлагает докачать по возможности файл. То есть разработчик должен предусмотреть не просто вывод «сообщение об ошибке», а реакцию, действие в результате этой ошибки.
Кстати, еще одна смешная ошибка, которую выдает операционная система, — «программа выполнила недопустимую операцию и будет закрыта». Если вдуматься, абсолютно бессмысленное сообщение. Во-первых, я даже и не предполагал, что программа выполняет какую-то операцию, тем более кем-то и зачем-то недопустимую. Не буду рассуждать долго на эту тему********, так как мне могут возразить, что если разрешить дальнейшее выполнение этой программы, то это приведет к потере информационной безопасности и вообще краху системы в целом.
******** В данном случае просто приведу конкретный пример. Internet Explorer вдруг перестал запускаться, и операционная система постоянно, через некоторое время после запуска, закрывала программу с сообщением о страшной ошибке с недопустимой операцией. Некоторое время я ломал голову — в чем дело? Даже по глупости переустанавливал программу. Как ни странно — не помогло. Потом я решил сделать вот что. Программа при запуске показывает рамку окна запускаемой формы с пустой внутренней областью, и только потом появляется все внутреннее наполнение окна. В момент, пока это наполнение не появилось, я, нажав правую кнопку мышки, вызвал контекстное меню и стал отключать установленные дополнительные панели. Не спорю, что есть другие способы решения этой проблемы. Я выбрал именно такой; пожалуй, средний пользователь сделал бы так же. Наконец, отключив какую-то панель, я увидел, что приложение замечательн
о запустилось и заработало без ошибок, как и раньше.
В итоге у меня возник вопрос: почему во многих программах, кроме операционной системы, не предусмотрен запуск в «безопасном режиме», который гарантированно и железно работает? А вместо этого выдается стандартное сообщение об ошибке? По крайней мере, это можно решить так: фиксировать число запусков и закрытий самым первым действием при запуске. Если это число не совпадает какое-то число раз (не исключен случай принудительного закрытия программы из диспетчера задач), то запускать программу в «безопасном режиме» и пытаться искать причину «поломки».
Итак, думаю, понятно, что просто сообщить о собственно ошибке пользователю — это ничего не сделать. Гораздо лучше сделать и ничего об этом не говорить, а молча про себя улыбнуться и подумать: «Какой я молодец!»
Заключение
Разговор об интерфейсе я только начал. И только этой одной статьей я не ограничусь, тем более что я больше не сказал, чем сказал, а объем получился большим. Что сказать и написать, у меня еще найдется. Конечно же, огромную помощь в написании статьи я получаю из книг и Интернета. По возможности, я указываю оригинальные источники, тем не менее стараюсь пересказывать своими словами ту информацию, которую узнал, чтобы это не было глупым плагиатом. До встречи в следующих статьях!
Ресурсы
- Об оформлении программной документации
http://www.raai.org/about/persons/karpov/pages - ГОСТ 19.102-77. Стадии разработки http://www.nist.ru/hr/doc/gost/19102-77.htm
- Студия Артемия Лебедева. Создание интерфейса навигатора «Штурман» http://www.artlebedev.ru/everything/shturmann/
- Этот мерзкий, неудобный, противоестественный оконный интерфейс http://epikoiros.narod.ru/public/antiwind.htm
- Обзор эргономических проблем и недостатков пользовательского интерфейса ПО бухгалтерского учета на примере 1С:Предприятие 7.5 http://www.usability.ru/
Статья из шестого выпуска журнала «ПРОграммист».
Обсудить на форуме – Разработчик интерфейс пользователи. Часть 2
23rd
Сен
Программы для самых маленьких
Компьютер в настоящее время для большинства уже перестал быть чем-то диковинным. Он используется и для развлечений, и для работы, а среди его пользователей школьники и студенты, любители и профессионалы, люди разных возрастов и профессий. Идея использовать компьютер, а точнее компьютерные программы для обучения и развития, естественно, родилась и реализуется уже давно. И вот, видя, с каким интересом мой маленький внук (а было ему тогда всего лишь 3,5 годика) наблюдает, как я работаю за компьютером, и как он старается тоже подвигать мышкой или понажимать клавиши, я понял: пора использовать его интерес для развития. Первым делом, конечно, поискал в Интернете программы, обучающие ребенка счету, рисованию и раскраске простеньких рисунков, знакомству с азбукой. Увы, результат оказался плачевным. Большинство программ, найденных мной в Интернете, или платные, или настолько сложные для ребенка, что и взрослому не всегда удается разобраться, как ею пользоваться. Так и появилась идея самому создать развивающие программы для маленьких с учетом интересов и способностей самого ребенка. Вскоре появились красочные программы “Раскраска” и “Азбука”, очень понравившиеся малышу. Яркие рисунки с любимыми игрушками и персонажами сказок и мультфильмов, простота управления программой – все это привлекает ребенка, прививая ему первые навыки координации движений, правильного выбора действий.
Владимир Дегтярь
by DeKot degvv@mail.ru
Целью данной статьи является помощь начинающим программистам в создании полноценных программ для маленьких детей. Вы можете просто скачать из приложения к журналу файлы *.exe (не забыв про ресурсы программы – папку Image с рисунками) и предложить поиграть своим маленьким пользователям.
В данной статье рассмотрим создание двух небольших программ, которые наверняка понравятся малышам. Думаю, что тем, кто хоть немного знаком с компьютером (тем более с программированием), не составит большого труда заменить или добавить рисунки, более подходящие для конкретного ребенка.
Программа “Раскраска”
Программа* представляет из себя, в прямом смысле, обычную раскраску, где ребенок выбирает понравившийся рисунок и раскрашивает его выбранными им же цветами.
* Комментарий автора.
Сразу отмечу, что данная программа обсуждалась на форуме http://programmersforum.ru/showthread.php?t=80497&highlight=%D0%E0%F1%EA%F0%E0%F1%EA%E0. Форумчанин raxp устранил некоторые ошибки и добавил ряд красивых рисунков, а mutabor заменил стандартный курсор на симпатичную кисточку.
Прежде чем приступить к самому процессу программирования, определимся со структурой программы. А именно, что же мы хотим получить от программы:
- Все действия будем производить на форме, точнее на канве формы. Следовательно, будем использовать в программе свойства и методы работы с канвой формы – вывод рисунков на форму (методы Draw, StretchDraw), заливку области канвы цветом (свойства кисти Brush.Color, метод заливки FloodFill и т. п.).
- Нам понадобятся рисунки для раскраски. При этом каждый рисунок будет иметь два вида. Один – это полноцветный рисунок, используемый для выбора и для подсказки (чтобы ребенок видел, каким цветом закрашивать фрагменты рисунка). Второй – непосредственно сам выбранный рисунок для раскраски в виде контуров фрагментов рисунка. Все контуры должны образовывать замкнутую область.
- К выбору рисунков следует подходить индивидуально для каждого ребенка. Кому-то нравятся машинки, самолеты, кораблики, кому-то – персонажи сказок и мультфильмов. Самым маленьким вполне достаточно простейших рисунков – солнышка, шарика, звездочки и т. п. Множество рисунков можно найти в Интернете. Это и готовые рисунки для раскрасок, и просто красочные картинки с различными персонажами.
- Еще нам потребуется набор палитры цветов. Естественно, что применение, скажем, стандартного компонента выбора палитры цветов вряд ли будет интересно для ребенка. Куда привлекательней, если он будет выбирать краску из разноцветных ведерочек.
Все действия в программе производим при помощи мыши – курсором, а затем кнопкой мыши выбираем или меняем рисунок, цвет заливки, закрашиваемую область рисунка. Вместо стандартного системного курсора будем использовать самодельный в виде кисточки.
* Комментарий автора.
Казалось бы, детской ручке тяжело манипулировать мышкой (даже если подобрать мышку самых маленьких размеров). Однако мой внук быстро сообразил, что удобно передвигать мышку и управлять курсором одной рукой, а второй нажимать кнопку мыши в нужном месте.
Вот, в принципе, и все основные пункты структуры программы. Итак, начнем постепенно создавать саму программу…
Предварительно следует подобрать рисунки для раскраски. Для получения из цветного рисунка контурного для раскрашивания можно применить простой способ. Откройте цветной рисунок в графическом редакторе Paint, а затем сохраните его как черно-белый рисунок. При необходимости можно залить области рисунка белым цветом и подправить контурные линии, чтобы получить замкнутые области. Все рисунки храним в папке Image в файлах формата bmp, которую расположим в папке с проектом. Для выбора рисунка выводим на форму одновременно 12 рисунков. При смене рисунков выводим следующую группу из 12 рисунков. При этом совсем необязательно, чтобы размеры рисунков были одинаковыми. При выводе рисунков на форму будем подгонять размер рисунка до необходимого. Файлам цветных рисунков, для удобства обработки в коде программы, присвоим цифровые имена. Для первой группы цветных рисунков – 1.bmp… 12.bmp, для второй – 21.bmp… 32.bmp, для следующей – 41.bmp… 52.bmp и т. д.
Файлы черно-белых рисунков для раскрашивания соответственно имеют имена р1.bmp… р12.bmp, р21.bmp… р32.bmp, р41.bmp… р52.bmp. Здесь же находятся еще два вспомогательных файла – рисунок пока пустого ведерка для краски col.bmp и изображение курсора в виде кисточки brush.bmp.
Рис. 1. Окно программы “Раскраска”
Окно программы состоит из трех частей (см. рисунок 1):
- Область выбора рисунка. Здесь расположены 12 цветных рисунков (размером 100х100 пикселей) для выбора и кнопка смены рисунков (в виде зеленого треугольника).
- Область выбора цвета – 15 разноцветных ведерок с красками.
- Область выбранного рисунка для раскраски. Здесь выводится выбранный рисунок (размером 600х600 пикселей).
Определимся, какие процедуры нам понадобятся для реализации программы. Так как все манипуляции в программе осуществляются при помощи мыши, будем использовать стандартные обработчики FormMouseDown() и FormMouseMove(), а также процедуру обработчика событий FormClick(). Вывод всех рисунков, а также курсора в виде кисточки будем производить в стандартных процедурах для окна формы FormShow() и FormPaint(). Еще две стандартные процедуры: FormCreate() используется для инициализации приложения после запуска, SetBrushColor – для окрашивания кисточки-курсора в выбранный цвет.
Две процедуры мы создадим сами. Одна из них – процедура выбора рисунка SelPicture для считывания цветных рисунков из файлов папки Image. Данная процедура вызывается при запуске (инициализации) приложения и каждый раз при смене рисунков выбора (когда нажимается треугольная кнопка смены рисунков). Вторая – процедура выбора области окна программы SelRegion. В зависимости от того, в какой области окна мы нажмем кнопку мыши, происходит одно из четырех событий:
- Выбор рисунка для закрашивания.
- Смена блока из 12 рисунков для выбора.
- Выбор цвета для закрашивания.
- Сама процедура закрашивания фрагментов выбранного рисунка.
В качестве параметра в процедуру SelRegion при нажатии кнопки мыши передаются координаты курсора мыши. Определимся с необходимыми нам переменными…
Так как все необходимые рисунки находятся в виде файлов, то будем по необходимости загружать их в объекты типа TbitMap: BufCol – изображения ведерка с краской, MyBrush – курсор-кисточка, Buffer – буфер для загрузки всех изображений, кроме курсора, и BufWin – буфер окна программы, из которого все изображения выводятся на форму.
Значения цветов будем хранить в одномерном массиве mas_col. Также нам понадобятся координаты курсора в момент нажатия кнопки мыши (xm и ym) и еще одна переменная типа byte – page. По ней будем определять имена файлов цветных рисунков при смене блоков рисунков (по 12 шт.). Все это переменные глобального типа (см. код ниже).
Form1: TForm1;
BufCol: TBitMap; // буфер загрузки ведерка с краской
MyBrush: TBitmap; // буфер загрузки курсора-кисточки
Buffer: TBitmap; // буфер загрузки всех компонентов, кроме кисточки
BufWin: TBitmap; // общий буфер вывода окна программы на форму
xm,ym: integer; // координаты курсора мыши
mas_col: array
[0..14] of integer; // массив палитры цветов
page: byte; // страница блока из 12 рисунков
Необходимые локальные переменные будем рассматривать по ходу описания кода приложения. Сначала изучим процедуры выборов рисунка и области окна приложения.
1. Выбор рисунков SelPicture
var i: byte; // переменная цикла
BufPic: TBitMap; // буфер загрузки цветных рисунков
x,y: integer; // координаты рисунков на канве буфера
begin
BufPic:=TBitmap.
Create; // создаем буфер
x:= 10; y:= 20; // координаты первого из 12 рисунков
for i:= 1 to 12 do // цикл загрузки рисунков
begin
BufPic.LoadFromFile(
‘Image\’ + IntToStr(i+page) + ‘.bmp’);
Buffer.Canvas.StretchDraw(bounds(x,y,100,100),BufPic); // вывод на канву Buffer
x:= x + 105;
if x > 115
then begin x:= 10; y:= y + 105; end;
end;
BufPic.Free; // освобождаем память
end;
В буфер BufPic типа TBitMap в цикле загружаем из файлов 12 цветных рисунков и помещаем на канву основного буфера Buffer, приводя к размеру 100х100 пикселей каждый. Имя файла определяется значением переменной цикла и значением страницы блоков из 12 рисунков: (i + page).bmp. Значение переменной page при запуске приложения равно нулю. При нажатии кнопки смены рисунков значение page увеличивается на 20 и при значениях >40 опять принимает значение, равное 0 (организовано в процедуре выбора области окна приложения).
2. Выбор области окна приложения
Процедура SelRegion вызывается по событию OnClick на форме, если мы нажимаем кнопку мыши в области выбора рисунка или в области выбора цвета (см. Рис. 1). В зависимости от координат курсора в момент нажатия кнопки мыши происходит следующее:
- “Клик” по кнопке выбора рисунков – изменяется значение переменной page ( 0 – 20 – 40 – 0 – ) и вызывается процедура SelPicture для загрузки других 12 рисунков;
- “Клик” по одному из цветных рисунков – определяется имя файла рисунка для раскраски по локальной переменной num, загружаем файл в буфер рисунка BufPic, а затем переносим в основной буфер Buffer (размер рисунка 600х600 пикселей) в область раскраски.
- “Клик” по любому из цветных ведерок – происходит выбор цвета, и цвет передается на канву основного буфера Buffer и на канву формы.
Теперь рассмотрим работу программы. При запуске приложения в процедуре FormCreate происходит инициализация:
- создаются объекты типа TBitMap для загрузки графических изображений;
- рисунки ведерка и курсора-кисточки загружаются из файлов в соответствующие буферы BufCol и MyBrush;
- заполняем массив mas_col значениями цветов.
В процедуре FormShow() присваиваются атрибуты основным буферам Buffer и BufWin – размеры, равные размерам клиентской области формы, и цвет канвы. Здесь же выводим на канву буфера Buffer 15 цветных ведерок для выбора цвета, цветные рисунки выбора, затем рисуем кнопку смены рисунков и прямоугольник области рисунка для раскрашивания.
В процедуре FormPaint() считываем координаты курсора (GetCursorPos(P: TPoint)) в пределах клиентской области формы, содержимое буфера Buffer копируем в буфер окна BufWin и в него же добавляем изображение курсора-кисточки. Затем содержимое BufWin выводим на форму. Далее управление программой осуществляет пользователь с помощью мыши. При перемещении мыши обрабатывается событие FormMouseMove(), в буфер окна BufWin копируется содержание основного буфера Buffer и накладывается новое положение курсора-кисточки, а затем все это выводится на форму.
При нажатии кнопки мыши вызываются два обработчика события – FormMouseDown(), в котором переменным xm и ym присваиваются значения координат курсора, и FormClick(), в котором, если курсор находится в области окна для раскрашивания рисунка, происходит заливка замкнутой области, где находится курсор, выбранным ранее цветом (метод FloodFill). При нахождении курсора в любой другой области окна и нажатии кнопки мыши вызывается процедура SelRegion, работу которой мы уже рассмотрели выше. При этом координаты мыши передаются в процедуру SelRegion (xm,ym) в качестве параметра.
И последняя процедура SetBrushColor(C: Tcolor) устанавливает для курсора-кисточки выбранный цвет. Более подробно весь код приложения можно посмотреть в приложении в файле <Unit1.pas>, который содержит подробные комментарии кода***.
* Комментарий автора.
Считаю, что нет необходимости добавлять в программу какие-либо “красивости” (разве, что добавить звуковое оформление), так как маленькому ребенку в первую очередь интересны предложенные красочные рисунки и простота управления программой.
Программа “Азбука”
Данная программа знакомит ребенка с буквами алфавита, помогая быстрее их запоминать и составлять простые слова. Чтобы понять смысл программы, рассмотрим окно приложения (см. Рис. 2):
Рис. 2. Окно программы “Азбука”
Оно разбито на четыре части:
- Область выбора рисунка (1) – здесь выводятся два рисунка или фотографии знакомых ребенку игрушек или персонажей сказок, мультфильмов, которые можно выбрать, “кликнув” по изображению. Также здесь находятся кнопки (в виде треугольников) смены рисунков или фотографий. Также можно взять фотографии родителей или домашних питомцев.
- Область выбранного рисунка с подписью (2), которая повторяется в поле вывода слова (3) по мере того, как ребенок будет правильно выбирать буквы из алфавита (4) в верхней части окна программы. В программе используются не все буквы алфавита, а только наиболее употребляемые. Кроме того, можно заменить русский алфавит на любой другой национальный (украинский, казахский и т. д.).
Суть программы в том, что после выбора изображения ребенку нужно повторить подпись к выбранному рисунку, нажимая на соответствующие буквы в поле алфавита. При правильном выборе букв слово-подпись повторяется в поле вывода. В качестве подсказки очередная буква подписи к рисунку мигает.
Аналогично предыдущей программе сначала опишем ее структуру и особенности.
- Все действия программы производим на канве формы.
- В папке Image проекта храним набор рисунков и фотографий, которые по мере необходимости выводим на форму.
- Все манипуляции в программе осуществляются мышью.
Процедуры для реализации программы: аналогично предыдущей программе будем использовать стандартные обработчики FormMouseDown() для получения координат курсора мыши, а также процедуру обработчика событий FormClick(), в которой обрабатываем все манипуляции мышкой в окне формы. Стандартная процедура FormCreate() используется для инициализации приложения после запуска, а в процедуре FormPaint(). выводим все необходимые изображения на форму. В обработчике таймера Timer1.Timer() организуем подсказку в виде мигания очередной буквы в слове-подписи. Кроме этих стандартных процедур, напишем еще две. SelAlfavit – процедура выбора буквы в поле алфавита. В качестве параметров в процедуру передаются координаты курсора, и по ним из алфавита выбирается буква. Если выбранная буква совпадает с очередной буквой подписи (для подсказки ребенку она мигает), то выбранная буква выводится в поле вывода слова. В процедуре SelFoto производится выбор рисунка или фото и вывод его
изображения на форму.
Заключение
Программа настолько проста, что нет смысла подробно рассматривать каждую процедуру. Весь код программы с подробными комментариями смотрите в файле Unit1.pas в виде ресурсов в теме “Журнал клуба программистов. Шестой выпуск” или непосредственно в архиве с журналом.
Как известно, готовность ребенка к школе определяется комплексом его общей интеллектуальной, психологической и физической подготовки. Причем, психологическая – одна из самых трудных, так как она не возникает сама по себе и требует особого внимания родителей. В чем она проявляется? Все просто как дважды-два и через эти проблемы каждый раз проходят и родители и преподаватели и сам ребенок. Это и отсутствие сосредоточенности ребенка на предмете и личной инициативы, частое отвлечение внимания. Быть готовым к школе – это не только уметь читать, писать и считать. Это прежде всего, готовность всему этому учиться.
Для детей дошкольного возраста очень важна усидчивость и способность доводить дело до конечной цели. В этом плане хорошо помогают разнообразные настольные игры, вроде конструктора или лепки поделок из пластилина или глины, игры на счет, используя аппликации, палочки и тому подобные. Что примечательно, эти же игры помогают развивать и мелкую моторику пальцев.
Сегодня, в свете огромного скачка вперед в информационных технологиях, компьютер есть практически в каждой семье. И нельзя отрицать тот факт, что использование его на школьных занятиях практикуется уже повсеместно. Так почему-бы не познакомить ребенка с чудом двадцатого века раньше и дать возможность заранее адаптироваться к школьным трудностям. Также не будем забывать про акселерацию детей.
Конечно же, все это должно быть в форме игры и обязательно под присмотром старших. Для развития мелкой моторики малыша не нужно дорогих игрушек и особого подхода. Достаточно поиграть в:
1) пальчиковый театр;
2) электронную раскраску или говорящий алфавит.
Именно, о них и шла речь в представленном материале. Мы не предлагаем панацею, а лишь показываем, что такая область как программирование полезна и в детском творчестве. Примечательно, что этот номер вышел как раз в начале учебного года. Надеемся, статья нашего постоянного автора Владимира Дегтяря поможет вам в игровой форме занять ваших малышей, а вам самим подарить несколько свободных минут. Удачи!
Статья из шестого выпуска журнала “ПРОграммист”.
23rd
Компилятор домашнего приготовления. Часть 1
Почему мне пришла в голову идея разработать собственный компилятор? Однажды мне на глаза попалась книга, где описывались примеры проектирования в AutoCAD на встроенном в него языке AutoLISP. Я захотел c ними разобраться, но прежде меня заинтересовал сам ЛИСП. “Неплохо бы поближе познакомиться с ним”, – подумал я и начал подыскивать литературу и среду разработки. С литературой все оказалось просто – по ЛИСПу ее море в Интернете. Достаточно зайти на портал [1]. Дело оставалось за малым – найти хорошую среду программирования, и вот тут-то начались трудности. Компиляторов под ЛИСП тоже немало, но все они оказались мне малопонятны. Ни один пример из Вики, по разным причинам, не отработал нормально в скачанных мною компиляторах. Собственно, серьезно я с ними не разбирался, но, увы, во многих не нашел как скомпилировать EXE-файл. Самое интересное, что компиляторы эти были собраны разными людьми практически в домашних условиях…
Виталий Белик
by Stilet
И мне пришла в голову мысль: а почему бы не попробовать самому написать свой компилятор или, основываясь на каком-либо диалекте какого-либо языка, свой собственный язык программирования? К тому же на форумах я часто видел темы, где слезно жаловались на тиранов-преподавателей, поставивших задачу написания курсовой – компилятора или эвалюатора (программы, вычисляющей введенное в виде строки выражение). Мне стало еще интереснее: а что если простому студенту, не искушенному книгами Вирта или Страуструпа, написать такую программу? Появился мотив.
In the Beginning
Итак, начнем. Прежде всего, нужно поставить задачу хотя бы на первом этапе. Задача будет банальная: доказать самому себе, что написание компилятора не такой уж сложный и страшный процесс. И что мы, хитрые и смекалистые, способны родить в муках собственного творчества шедевр, который, возможно, полюбится массам. Да и вообще: приятно писать программы на собственном языке, не так ли?
Что ж, цель поставлена. Теперь самое время определиться со следующими пунктами:
- Под какую платформу будет компилировать код программа?
- На каком языке будет код, переводимый в машинный язык?
- На чем будем писать сам компилятор?
Первый пункт достаточно важен, ибо широкое разнообразие операционных систем (даже три монстра – Windows, Linux и MacOS) уже путают все карты. Их исполняемые файлы по-разному устроены, так что нам, простым смертным, придется выбрать из этой “кагалы” одну операционную систему и, соответственно, ее формат исполняемых файлов. Я предлагаю начать с Windows, просто потому, что мне нравится эта операционная система более других. Это не значит, что я терпеть не могу Linux, просто я его не очень хорошо знаю, а такие начинания лучше делать по максимуму, зная систему, для которой проектируешь.
Два остальных пункта уже не так важны. В конце концов, можно придумать свой собственный диалект языка. Я предлагаю взять один из старейших языков программирования – LISP. Из всех языков, что я знаю, он мне кажется более простым по синтаксису, более атомарным, ибо в нем каждая операция берется в скобочки; таким образом, к нему проще написать анализатор. С выбором, на чем писать, еще проще: писать нужно на том языке, который лучше всего знаешь. Мне ближе паскалевидные языки, я хорошо знаю Delphi, поэтому в своей разработке я избираю именно его, хотя никто не мешает сделать то же самое на Си. Оба языка прекрасно подходят для написания такого рода программ. Я не беру в расчет Ассемблер потому, что его диалект приближен к машинному языку, а не к человеческому.
To Shopping
Выяснив платформу, для которой будем писать компилятор (я имею в виду Win32), подберем все необходимое для комфортной работы. Давайте составим список, что же нам пригодится в наших изысканиях.
Для начала нам просто крайне необходимо выяснить, как же все-таки компиляторы генерируют исполняемые EXE-файлы под Windows. Для этого стоит почитать немного об устройстве этих “экзэшек”, как их часто называют, покопаться в их “кишках”. В этом могут помочь современные отладчики и дизассемблеры, способные показать, из чего состоит “экзэшка”. Я знаю два, на мой взгляд, лучших инструмента: OllyDebugger (он же “Оля”) и The Interactive Disassembler (в простонародье зовущийся IDA).
Оба инструмента можно достать на их официальных сайтах http://www.ollydbg.de/ и http://www.hex-rays.com/idapro. Они помогут нам заглянуть в святая святых – храм, почитаемый загрузчиком исполнимых файлов, – и посмотреть, каков интерьер этого храма, дабы загрузчик наших экзэшек чувствовал себя в нем так же комфортно, как “ковбой в подгузниках Хаггис”.
Также нам понадобится какая-нибудь экзэшка в качестве жертвы, которую мы будем препарировать этими скальпелями-дизассемблерами. Здесь все сложнее. Дело в том, что благородные компиляторы имеют дурную привычку пихать в экзэшник, помимо необходимого для работы кода, всякую всячину, зачастую ненужную. Это, конечно, не мусор, но без него вполне можно обойтись, а вот для нашего исследования внутренностей экзэшек он может стать серьезной помехой. Мы ведь не Ричарды Столлманы и искусством реверсинга в совершенстве не владеем. Поэтому нам лучше было бы найти такую программу, которая содержала бы в себе как можно меньше откомпилированного кода, дабы не отвлекаться на него. В этом нам может помочь компилятор Ассемблера для Windows. Я знаю два неплохих компилятора: Macro Assembler (он же MASM) и Flat Assembler (он же FASM). Я лично предпочитаю второй – у него меньше мороки при компилировании программы, есть собственный редактор, в отличие от MASM компиляция проходит нажатием одной-единственной кнопки. Для MASM разработаны среды проектирования, например MASM Builder. Это достаточно неплохой визуальный инструмент, где на форму можно кидать компоненты по типу Delphi или Visual Studio, но, увы, не лишенный багов. Поэтому воспользуемся FASM. Скачать его можно везде, это свободно распространяемый инструмент. Ну и, конечно, не забудем о среде, на которой и будет написан наш компилятор. Я уже сказал, что это будет Delphi. Если хотите конкретнее – Delphi 6.
The Theory and Researching
Прежде чем приступить к написанию компилятора, неплохо бы узнать, что это за формат “экзэшка” такой. Согласно [2], Windows использует некий PE-формат. Это расширение ранее применявшегося в MS-DOS, так называемого MZ формата [3]. Сам чистый MZ-формат простой и незатейливый – это 32 байта (в минимальном виде, если верить FASM; Турбо Паскаль может побольше запросить), где содержится описание для DOS-загрузчика. В Windows его решили оставить, видимо, для совместимости со старыми программами. Вообще, если честно, размер DOS-заголовка может варьироваться в зависимости от того, что после этих 28 байт напихает компилятор. Это может быть самая разнообразная информация, например для операционок, которые не смогли бы использовать скомпилированный DOS или Windows-экзэшник, представленная в качестве машинного кода, который прерываниями BIOS выводит на экран надпись типа “Эта программа не может быть запущена…”. Кстати, сегодняшние компиляторы поступают так же.
Давайте посмотрим на это чудо техники, воспользовавшись простенькой программой, написанной на чистом Ассемблере FASM (см. Рис. 1):
Рис. 1. Исходник для препарирования
Сохраним файл под неким именем, например Dumpy. Нажмем F9 или выберем в меню пункт RUN. В той же папке будет создан EXE-файл. Это и будет наша жертва, которую мы будем препарировать. Теперь ничто не мешает нам посмотреть: “из чего же, из чего же сделаны наши девчонки?”.
Запустим OllyDebuger. Откроем в “Оле” наш экзэшник. Поскольку фактически кода в нем нет, нас будет интересовать его устройство, его структура. В меню View есть пункт Memory, после выбора которого “Оля” любезно покажет структуру загруженного файла (см. Рис. 2):
Рис. 2. Карта памяти Dumpy
Это не только сам файл, но и все, что было загружено и применено вместе с ним, библиотеки, ресурсы программы и библиотек, стек и прочее, разбитое на блоки, называемые секциями. Из всего этого нас будут интересовать три секции, владелец которых Dumpy, – это непосредственно содержимое загруженного файла.
Собственно, эти секции были описаны нами в исходнике, я не зря назвал их по-русски (ведь операционной системе все равно, как названы секции, главное – их имена должны укладываться точь-в-точь в 8 байт. Это придется учесть обязательно).
Заглянем в первую секцию PE Header. Сразу же можем увидеть (см. Рис. 3), что умная “Оля” подсказывает нам, какие поля* у этой структуры:
Рис. 3. MZ-заголовок
Сразу хочу оговориться, не все из этих полей нам важны. Тем паче что сам Windows использует из них от силы 2-3 поля. Прежде всего, это DOS EXE Signature – здесь (читайте в Википедии по ссылке выше) помещаются две буквы MZ – инициалы создателя MS-DOS, и поле DOS_PartPag. В нем указывается размер MZ-заголовка в байтах, после которых помещается уже PE-заголовок.
Последнее поле, которое для нас важно, находится по смещению 3Ch от начала файла (см. Рис. 4):
Рис. 4. Смещение на PE-заголовок
Это поле – точка начала РЕ-заголовка. В Windows, в отличие от MS-DOS, MZ-заголовок заканчивается именно на отметке 40**, что соответствует 64 байтам. При написании компилятора будем соблюдать это правило неукоснительно.
Обратите внимание! Далее, с 40-го смещения, “Оля” показывает какую-то белиберду. Эта белиберда есть атавизм DOS и представляет из себя оговоренную выше информацию, с сообщением о том, что данная программа может быть запущена только под DOS-Windows. Этакий перехватчик ошибок. Как показывает практика, этот мусор можно без сожаления выкинуть. Наш компилятор не будет генерировать его, сразу переходя к PE-заголовку.
Что ж, перейдем непосредственно к PE-заголовку (см. Рис. 5). Как показывает “Оля”, нам нужно перейти на 80-й байт. Да, чуть не забыл. Все числа адресации указываются в 16-тиричной системе счисления. Для этого после чисел ставится латинская буква “H”. “Оля” не показывает ее, принимая эту систему по умолчанию для адресации. Это нужно учесть, чтобы не запутаться в исследованиях. Фактически 80h – это 128-й байт.
Рис. 5. Начало РЕ-заголовка
Вот она, святая обитель характеристик экзэшника. Именно этой информацией пользуется загрузчик Windows, чтобы расположить файл в памяти и выделить ему необходимую память для нужд. Вообще, считается, что этот формат хорошо описан в литературе. Достаточно выйти через Википедию по ссылкам в ее статьях [4] или банально забить в поисковик фразу вроде “ФОРМАТ ИСПОЛНЯЕМЫХ ФАЙЛОВ PortableExecutables (PE)”, как сразу же можно найти кучу описаний. Поэтому я поясню только основные его поля, которые нам понадобятся непосредственно для написания компилятора…
Прежде всего, это PE Signature – 4-хбайтовое поле. В разной литературе оно воспринимается по-разному. Иногда к нему приплюсовывают еще поле Machine, оговариваясь, чтобы выравнять до 8 байт. Мы же, как любители исследовать, доверимся “Оле” с “Идой” и будем разбирать поля непосредственно по их подсказкам. Это поле содержит две латинские буквы верхнего регистра “PE”, как бы намекая нам, что это Portable Executable-формат.
Следующее за ним поле указывает, для какого семейства процессоров пригоден данный код. Всего их, как показывает литература, 7 видов:
014Ch __80386
014Dh __80486
014Eh __80586
0162h __MIPS Mark I (R2000, R3000)
0163h __MIPS Mark II (R6000)
0166h __MIPS Mark III (R4000)
Думаю, нам стоит выбрать из всего этого второй вид – 80386. Кстати, наблюдательные личности могли заметить, что в компиляторах Ассемблера есть директива, указывающая, какое семейство процессора использовать, как, например, в MASM (см. Рис. 6):
Рис. 6. Указание семейства процессоров в МАСМ
386 как раз и говорит о том, что в этом поле будет стоять значение 014Ch***.
Обратите внимание на одну небольшую, но очень важную особенность: байты в файле непосредственно идут как бы в перевернутом виде. Вместо 14С в файл нужно писать байты в обратном порядке, начиная с младшего, т. е. получится 4С01 (0 здесь дополняет до байта. Это для человеческого глаза сделано, иначе все 16-тиричные редакторы показывали бы нестройные 4С1. (Согласитесь, трудно было понять, какие две цифры из этого числа к какому байту относятся.) Эту особенность обязательно придется учесть. Для простоты нелишним было бы написать пару функций, которые число превращают в такую вот перевернутую последовательность байт (что мы в дальнейшем и сделаем).
Следующее важное для нас поле – NumberOfSections. Это количество секций без учета PE-секции. Имеются в виду только те секции, которые принадлежат файлу (в карте памяти их владелец – Dumpy). В нашем случае это “Данные” и “код”.
Следующее поле хоть и не столь важно, но я его опишу. Это TimeDateStamp – поле, где хранится дата и время компиляции. Вообще, я его проигнорирую, не суть важно сейчас, когда был скомпилирован файл. Впрочем, если кому захочется помещать туда время, то флаг в руки.
Сразу хочу предупредить, что меня как исследователя не интересовали поля с нулевыми значениями. На данном этапе они действительно неважны, поэтому в компиляторе их и нужно будет занулить.
Следующее важное поле – SizeOfOptionalHeader. Оно содержит число, указывающее, сколько байт осталось до начала описания секций. В принципе, нас будет устраивать число 0Eh (224 байта).
Далее идет поле “характеристики экзэшника”. Мы и его будем считать константным:
И равно оно 010Eh. На этом поле заканчивается так называемый “файловый заголовок” и начинается “Опциональный”.
Следующее поле – MagicNumber. Это тоже константа. Так называемое магическое число. Если честно, я не очень понял, для чего оно служит, в разных источниках это поле преподносится по-разному, но все хором ссылаются на знаменитый дизассемблер HIEW, в котором якобы впервые появилось описание этого поля именно в таком виде. Примем на веру.
Следующие два поля, хоть и не нулевые, но нам малоинтересны. Это: MajorLinkerVersion и MinorLinkerVersion. Это два байта версии компилятора. Угадайте, что я туда поставил?
Следующее важное поле – AddressOfEntryPoint. Важность этого поля в том, что оно указывает на адрес, с которого начинается первая команда, – с нее процессор начнет выполнение. Дело в том, что на этапе компиляции значение этого поля не сразу известно. Ее формула достаточно проста. Сначала указывается адрес первой секции плюс ее размер. К ней плюсуются размеры остальных секций до секции, считаемой секцией кода. Например, в нашей жертве это выглядит так (см. Рис. 7):
Рис. 7. Расчет точки входа
Здесь секция кода вторая по счету, значит, Адрес точки входа равен размеру секции “Данные” плюс ее начало и равен 2000. К этому еще пририсовывается базовый адрес, в который загрузчик “сажает” файл, но он в вычислении для нашего компилятора не участвует. Поэтому в жертве точка входа имеет значение 2000.
Следующее поле – ImageBase. Это поле я приму как константу, хотя и не оговаривается ее однозначное значение. Это значение указывает адрес, с которого загрузчик поместит файл в память. Оно должно нацело делиться на 64000. В общем, необязательно указывать именно 400000h, можно и другой адрес. Уже не помню, где я слышал, что загрузчик может на свое усмотрение поменять это число, если вдруг в тот участок памяти нельзя будет загружать, но не будем это проверять, а примем на веру как константу =400000h.
Следующая важная константа – SectionAlignment. Это значение говорит о размере секций после загрузки. Принцип прост: каждая секция (имеется в виду ее реализация) дополняется загрузчиком пустыми байтами до числа, указанного в этом поле. Это так называемое выравнивание секций. Тут уж хороший компилятор должен думать самостоятельно, какой размер секций ему взять, чтобы все переменные (или сам код), которые в коде используются, поместились без проблем. Согласно спецификации, это число должно быть степенью двойки в пределах от 200h (512 байт) до 10000h (64 000 байт). В принципе, пока что для простенького компилятора можно принять это значение как константу. Нас вполне устроит среднее значение 1000h (4096 байт – не правда ли, расточительный мусор? На этом весь Windows построен – живет на широкую ногу, память экономить не умеет).
Далее следует поле FileAlignment. Это тоже хитрое поле. Оно содержит значение, сколько байт нужно дописать в конец каждой секции в сам файл, т. е. выравнивание секции, но уже в файле. Это значение тоже должно быть степенью двойки в пределах от 200h (512 байт) до 10000h (64 000 байт). Неплохо бы рассчитывать функцией это поле в зависимости от размеров, данных в секции.
Следующие поля – MajorSubsystemVersion и MinorSubsystemVersion – примем на веру как константы. 3h и Аh соответственно. Это версия операционной системы, под которую рассчитывается данная компиляция****.
Я не проверял на других ОС: у меня WinXP. В принципе можно не полениться и попробовать пооткрывать “Олей” разные программы, рассчитанные на другие версии Windows.
Далее из значимых следует SizeOfImage. Это размер всего заголовка, включая размер описания всех секций. Фактически это сумма PE-заголовка плюс его выравнивание, плюс сумма всех секций, учитывая их выравнивание. Ее тоже придется рассчитывать.
Следующее поле – SizeOfHeaders (pазмеp файла минус суммарный pазмеp описания всех секций в файле). В нашем случае это 1536-512 * 2=200h (512 байт). Однако РЕ тоже выравнен! Это поле тоже нужно будет рассчитывать.
Далее следует не менее коварное поле – CheckSum. Это CRC сумма файла. Ужас… Мы еще файл не создали, а нам уже нужно ее посчитать (опять-таки вспоминается Микрософт злым громким словом). Впрочем, и тут можно вывернуться. В Win API предусмотрена функция расчета CRC для области данных в памяти, проще говоря, массива байт – CheckSumMappedFile. Можно ей скормить наш эмбрион файла. Причем веселье в том, что эта операция должна быть самой последней до непосредственной записи в файл. Однако, как показывает практика, Windows глубоко наплевать на это поле, так что мы вполне можем не морочить себе голову этим расчетом (согласитесь, держать в файле поле, которое никому не нужно, да еще и напрягать нас лишним расчетом – это глупо, но, увы, в этом изюминка политики Микрософта. Складывается впечатление, что программисты, писавшие Windows, никак не согласовывали между собой стратегию. Спонтанно писали. Импровизировали).
Следующее поле – Subsystem. Может иметь следующие значения*****:
- IMAGE_SUBSYSTEM_WINDOWS_CUI=3. Это говорит о том, что наш откомпилированный экзэшник является консольной программой.
- IMAGE_SUBSYSTEM_WINDOWS_GUI=4. Это говорит о том, что экзэшник может создавать окна и оперировать сообщениями.
Для справки, кто хорошо знает Delphi: директивы компилятора {$APPTYPE GUI} и {$APPTYPE CONSOLE} именно эти параметры и выставляет.
Вот, собственно, и все важные для нас параметры. Остальные можно оставить константно, как показывает “Оля”:
SizeOfStackReserve = 1000h (4096)
SizeOfStackCommit = 1000h (4096)
SizeOfHeapReserve = 10000h (65536)
NumberOfRvaAndSizes = 10h (16)
И остаток забить нулями (посмотрите в “Оле” до начала секций, какие там еще параметры). О них можно почитать подробнее по ссылкам, которые я привел.
После идет описание секций. Каждое описание занимает 32 байта. Давайте взглянем на них (Рис. 8):
Рис. 8. Описание секций
В начале секции идет ее имя (8 байт), после этого поле – VirtualSize, описывает (я процитирую из уроков Iczeliona) “RVA-секции. PE-загpузчик использует значение в этом поле, когда мэппиpует секцию в память. То есть, если значение в этом поле pавняется 1000h и файл загpужен в 400000h, секция будет загpужена в 401000h”.
Однако “Оля” почему-то показывает для обеих секций одно и то же значение 9. Что это? Я не понял, почему так. Пока оставим это как данное. Вдруг в будущем разберемся.
Далее следует VirtualAddres, который указывает, с какого адреса плюс ImageBase будет начинаться в памяти секция – это важное поле, именно оно станет для нашего компилятора базой для расчета адреса к переменной. Собственно, адрес этот напрямую зависит от размера секции. Следующий параметр PointerToRawData – это смещение на начало секции в скомпилированном файле. Как я понял, этот параметр компиляторы любят подводить под FileAlignment. И последнее – поле Characteristics. Сюда прописывается доступ к секции. В нашем случае для секции кода оно будет равным 60000020=CODE|EXECUTE|READ, а для секции данных C0000040=INITIALIZED_DATA |READ|WRITE.
Вот и все. Закончилось описание заголовка. Далее он выравнивается нулями до 4095 байт (с этим числом связан один прикол). В файле мы его будем дополнять до FileAlignment (в нашем случае до 200h).
Hello world. Hey! Is There Anybody Out There?
Вот мы и прошлись по кишкам нашей жертвы – экзэшника. Напоследок попробуем на скорую руку закрутить простейший компилятор для DOS-системы без PE-заголовка. Для этого подойдут инструменты, которые так почему-то любят преподавать до сих пор.
Я говорю о классическом Паскале. Итак, предположим, злобный преподаватель поставил задачу: написать компилятор программы вывода на экран некой строки, которую мы опишем для компилятора, введя ее ручками (см. листинг 1):
e,i:integer;
f:file;
begin
{Это MZ-заголовок}
header:=#$4D#$5A#$3E#$00#$01#$00#$
00#$00#$02#$00#$00#$01#$FF#$FF#$02#$00#$00;
header:=header+#$10#$00#$00#
$00#$00#$00#$00#$1C#$00#$00#$00#$00#$00#$00#$00;
writeln(
‘give me welcome ’);readln(s);
{Поскольку у нас все в одном сегменте, и код и данные, лежащие непосредственно в конце кода, нужно, чтобы регистр, содержащий базу данных, указывал на код. Предположим, мы будем считать, что и сам код представляет из себя данные. Для этого поместим в стек адрес сегмента кода}
{*******************************************************************************************}
Commands:=#$0E; { push cs}
{и внесем из стека этот адрес в регистр сегмента данных}
Commands:=Commands+#$1F; { pop ds}
Commands:=Commands+#$B4#$09; { mov ah, 9 – Вызовем функцию вывода строки на экран}
{Передадим в регистр DX-адрес на строку. Поскольку пока что строка у нас не определена, передадим туда нули, а позже подкорректируем это место}
Commands:=Commands+#$BA#$
00#$00; { mov dx, }
{Запомним место, которое нужно будет скорректировать. Этим приемом я буду пользоваться, чтобы расставить адреса в коде, который обращается к переменным}
e:=length(commands)-1;
{Выведем на экран строку}
Commands:=Commands+#$CD#$21;
{ int 21h ; DOS – PRINT STRING}
{подождем, пока пользователь не нажмет любую клавишу}
Commands:=Commands+#$B4#$01; { mov ah, 1}
Commands:=Commands+#$CD#$21; { int 21h ; DOS – KEYBOARD INPUT}
{После чего корректно завершим программу средствами DOS}
Commands:=Commands+#$B4#$4C; { mov ah, 4Ch}
Commands:=Commands+#$CD#$21; {int 21h ; DOS – 2+ – QUIT WITH EXIT CODE (EXIT)}
Commands:=Commands+#$C3; {retn}
{*******************************************************************************************}
{Теперь будем править адреса, обращающиеся к переменной. Поскольку само значение переменной у нас после всего кода (и переменная) одно, мы получим длину уже имеющегося кода – это и будет смещение на начало переменной}
i:=length(commands);
{В запомненное место, куда нужно править, запишем в обратном порядке это смещение}
commands[e]:=chr(lo
(i));
commands[e+1]:=chr(hi(i));
{Учтем, что в DOS есть маленький атавизм – строки там должны завершаться символом $. По крайней мере, для этой функции.}
commands:=commands+s+‘$’;
{не забудем дописать в начало заголовок}
commands:=header+commands;
{Теперь скорректируем поле DOS_PartPag. Для DOS-программ оно указывает на общий размер файла. Честно говоря, я не знаю, зачем это было нужно авторам, может быть, когда они изобретали это, еще не было возможности получать размер файла из FAT. Опять-таки запишем в обратном порядке}
i:=length(commands);
commands[3]:=chr(lo(i))
;
commands[4]:=chr(hi(i));
{Ну, и кульминация этого апофигея – запись скомпилированного массива байт в файл. Все заметили, что я воспользовался типом String, – он в паскалевских языках был изначально развит наиудобнейшим образом}
Assign(f,‘File.exe’);rewrite(f);
BlockWrite(f,commands[1],length(commands), e);
Close(f);
end.
Не удивляет, что программа получилась небольшой? Почему-то преподаватели, дающие такое задание, уверены, что студент завалится. Думаю, такие преподаватели сами не смогли бы написать компилятор. А студенты смогут, ибо, как видим, самая большая сложность – это найти нужные машинные коды для решения задачи. А уж скомпилировать их в код, подкорректировать заголовок и расставить адреса переменных – задача второстепенной сложности. В изучении ассемблерных команд поможет любая книга по Ассемблеру. Например, книга Абеля “Ассемблер для IBM PC”. Еще неплохая книга есть у Питера Нортона, где он приводит список функций DOS и BIOS.
Впрочем, можно и банальнее. Наберите в поисковике фразу “команды ассемблера описание”. Первая же ссылка выведет нас на что-нибудь вроде [5] или [6], где описаны команды Ассемблера. Например, если преподаватель задал задачку написать компилятор сложения двух чисел, то наши действия будут следующими:
- Выясняем, какая команда складывает числа. Для этого заглянем в книгу того же Абеля, где дается такой пример:
сложение содержимого
ADD AX,25 ;Прибавить 25
ADD AX,25H ;Прибавить 37 - Значит, нам нужна команда ADD. Теперь определимся: нам же нужно сложить две переменные, а это ячейки памяти; эта команда не умеет складывать сразу из переменной в переменную, для нее нужно сначала слагаемое поместить в регистр (AX для этого лучше подходит), а уж потом суммировать в него. Для помещения из памяти в регистр (согласно тому же Абелю) нужна команда
mov [адрес], ax
- Таким образом, инструкции будут выглядеть так:
mov [Адрес первой переменной], ax
add [Адрес второй переменной], ax - Теперь нужно определиться с кодами этих команд. В комплекте с MASM идет хелп, где описаны команды и их опкоды (машинные коды, операционные коды). Вот, например, как выглядит опкод команды MOV из переменной:
Рис. 9. Опкоды MOV
Видим (см. Рис. 9), что его опкод A1 (тут тоже любят 16-тиричность). Таким образом, выяснив все коды, можно написать компилятор что-то вроде этого (см. листинг 2):
Commands:= Commands+#$A1#$00#$00; { mov [Из памяти] в AX}
aPos:= Length(Commands)-1;{Запомним позицию для корректировки переменной a}
Commands:= Commands+#$03#$06#$00#$00;
{ $03 – Это опкод команды ADD $06 – Это номер регистра AX}
bPos:= Length(Commands)-1;{Запомним позицию для корректировки переменной b}
Commands:= Commands+#$A3#$00#$00; { mov из AX в переменку b}
b2Pos:= Length(Commands)-1;
{Запомним позицию для корректировки для переменной b}А далее, в конце, скорректируем эти позиции (см. листинг 3):
commands:= commands+#$01#$00; {Это переменка a, ее значение}
i:= length(commands);
commands[aPos]:= chr(lo(i)); {Не забудем, что адреса в перевернутом виде}
commands[
aPos+1]:= chr(hi(i)); {Поэтому сначала запишем младший байт}
commands:= commands+#$02#$00; {Это переменка b, ее значение}
i:= length(commands);
commands[bPos]:= chr
(lo(i)); {Поскольку переменка b фигурирует в коде}
commands[bPos+1]:= chr(hi(i)); {дважды придется корректировать ее}
commands[b2Pos]:= chr
span style=”color: #66cc66;”>(lo(i));
commands[b2Pos+1]:= chr(hi(i));Запустим компилятор, он скомпилирует экзэшник, который посмотрим в “Иде” (см. Рис. 10):
Рис. 10. Реверсинг нашего кода
Все верно, в регистр пошло значение одной переменной, сложилось со второй и во вторую же записалось. Это эквивалентно паскалевскому b:=b+a;
Обратите внимание: значения переменных мы заранее проинициализировали. При желании можно сделать, чтобы компилятор спросил их инициальное значение, и подставить в нужное место:
Post Scriptum
Ну как, студенты, воспряли духом? Теперь понятен смысл, куда двигаться в случае столкновения с такими задачами? Это вряд ли потянет на курсовую, но вполне подойдет для простенькой контрольной. А вот следующая задача – написать транслятор – уже действительно тянет даже на хороший диплом, так что пока переварим все то, что выше, а в следующий раз попробуем приготовить основное ядро компилятора, дабы потом уже делать упор на сам код программы.
The Чтиво
- Ресурс Википедии по языку LISP http://ru.wikipedia.org/wiki/Lisp
- Ресурс Википедии по формату PE http://ru.wikipedia.org/wiki/PE
- Ресурс Википедии по формату MZ http://ru.wikipedia.org/wiki/MZ_(формат)
- Micheal J. O’Leary (Microsoft). The portable executable format http://www.nikse.dk/petxt.html
- Описание простейших команд Ассемблера http://students.uni-vologda.ac.ru/pages/it10/2.php
- Питер Абель. Ассемблер и программирование для IBM PC. – British Columbia Institute of Technology
Статья из шестого выпуска журнала “ПРОграммист”.
Обсудить на форуме — Компилятор домашнего приготовления. Часть 1
21st
Сен
О правильном составлении ТЗ. Часть 1
Рано или поздно в жизни программиста появляется заказчик с неким проектом. Разработчик берется за этот проект, но результатом каждой последующей встречи с заказчиком становится появление новых функциональных и нефункциональных требований, в то время как сроки сдачи и бюджет остаются прежними, таким образом, все наваливается снежным комом, и в итоге наступает коллапс. Для того, чтобы такая скверная ситуация не наступила, с заказчиком нужно договориться еще на берегу. Но нельзя полагаться на какие-то словесные договоренности и т. п.: мы должны максимальн
о себя обезопасить. Разрабатывая программу, мы преследуем определенную цель, ставим перед собой определенные задачи. Успех нашего дела во многом зависит от того, насколько наша цель будет четкой и ясной. Знакомясь с выполненной работой, заказчик, естественно, будет анализировать, выполнили ли мы поставленные задачи, поэтому все они должны быть четко сформулированы, описаны и согласованы с ним. Вот тут нам на помощь и приходит ТЗ.
Дарья Устюгова
by Sparky
Введение
В цикле статей, основываясь на своем опыте и на опыте других, я попробую рассказать, что такое ТЗ, зачем оно нам нужно, из чего состоит, как его написать и оформить, какие подводные камни можно обойти, написав грамотное ТЗ. В первой статье я постараюсь объяснить, зачем ТЗ используется, в чем его плюсы, кто его должен писать, и приведу примерное содержание ТЗ.
Техническое задание. Что это и зачем оно нам?
Начнем мы с определения: техническое задание (ТЗ) – точная формулировка задачи. Можно даже сказать, что ТЗ всему голова. Многие слышали о таком понятии, как жизненный цикл программного обеспечения (ПО); для тех, кто не слышал, а быть может, просто забыл, напомню: жизненный цикл ПО – системы процедур, правил и инструментальных средств, используемых для разработки и поддержания работоспособности программной системы. Жизненный цикл включает в себя этапы создания программы. Существует множество версий: по Глассу, по ГОСТ ЕСПД, по Майерсу. Все эти версии
19th
Сен
Исследование протокола PS/2 для мышки
В этой статье я попытаюсь объяснить аспекты, связанные с интерфейсом PS/2 для периферийного устройства типа мышь, включая физический, электрический интерфейс, протокол низкого уровня, режимы работы, команды и расширения…
Евгений Амосов
by Gambit_OZ gambitoz@mail.ru
Мышь воспринимает своё перемещение в рабочей плоскости (обычно – на участке поверхности стола) и передаёт эту информацию компьютеру. Программа, работающая на компьютере, в ответ на перемещение мыши производит на экране действие, отвечающее направлению и расстоянию этого перемещения. В универсальных интерфейсах (например, в оконных) с помощью мыши пользователь управляет специальным курсором – указателем, то есть манипулятором элементами интерфейса.
В дополнение к детектору перемещения мышь имеет от одной до трёх и более кнопок, а также дополнительные элементы управления (колёса прокрутки, потенциометры, джойстики, трекболы, клавиши и т. п.), действие которых обычно связывается с текущим положением курсора (или составляющих специфического интерфейса).
Фактически все эти устройства общаются с компьютером при помощи двух интерфейсов: универсальной последовательной шины (USB) или интерфейса PS/2.
Интерфейс PS/2 расшифровывается как Personal System/2. Широкую популярность он приобрел благодаря использованию для взаимодействия с манипулятором типа мышь в компьютерах Apple Macintosh и позднее в ОС Windows для IBM PC в конце 80-х. Однако сегодня быстро завоевавший популярность интерфейс USB в конечном итоге практически заменил PS/2 полностью. Тем не менее, протокол PS/2 представляет собой интересную платформу для различных экспериментов научного характера. Например, можно использовать мышку с разъемом PS/2 для управления каким-либо передвижным устройством-роботом или сделать свой манипулятор, который при помощи датчиков-гироскопов, закрепленных на пальцах руки, будет эмулировать передвижение курсора на экране, а щелчком пальцев открывать папки и т. д. В дальнейших статьях будет описано, как это можно реализовать.
Интерфейс мыши PS/2 использует двунаправленный последовательный протокол, по которому передаются данные о движении и состоянии кнопок вспомогательному диспетчеру устройств компьютера (далее по тексту – хост). Диспетчер, в свою очередь, посылает команды для мыши, чтобы установить частоту обмена, разрешение, ее перезагрузку, выключение и т. д. Напряжение питания на линии передачи 5В ~100 мА. На Рис. 1 показано обозначение контактов разъема PS/2, на Рис. 2 – возможное подключение мышки к своему устройству:
Рис. 1. Обозначение контактов PS/2
Рис. 2. Вариант подключения мышки к своему устройству
Входы, разрешение и масштабирование
Стандартный интерфейс мыши PS/2 поддерживает следующие входы: перемещение по координате X (вправо/влево), перемещение по координате Y (вверх/вниз), левая кнопка, средняя кнопка, правая кнопка. Данный тип мыши является стандартом, но сейчас уже не используется. Мышь периодически читает эти входы, обновляет связанные с ними счетчики и устанавливает флаги, чтобы в дальнейшем отправить хосту текущее состояние кнопок и перемещение. Есть много манипуляторов PS/2, которые имеют дополнительные входы и могут сообщить о данных по-другому, чем описано в этом документе. Одним из популярных расширений является Microsoft Intellimouse, которое включает в себя поддержку стандартных входов, а также колеса прокрутки и двух дополнительных кнопок (это уже современные мышки).
У стандартной мыши есть два 9-разрядных счетчика с флагами переполнения, которые отслеживают перемещение: по координате X и по координате Y. Их содержание, а также состояние трех кнопок мыши отправляется хосту в виде 3-хбайтового пакета данных. Счетчики перемещения определяют смещение мыши относительно ее предыдущей позиции, когда было совершено перемещение или когда пришла команда Resend (0xFE).
Когда мышь читает свои входы, она записывает текущее состояние своих кнопок и постепенно увеличивает/уменьшает счетчики перемещения согласно значению перемещения, которое произошло относительно последнего значения. Если любой из счетчиков переполнится, то будет установлен соответствующий флаг переполнения.
Параметр, который определяет величину увеличения/уменьшения счетчиков перемещения, является разрешением. Разрешение по умолчанию равно четырем значениям на миллиметр, и хост может изменить это значение, используя команду Set Resolution (0xE8).
Существует параметр, который не влияет на счетчики перемещения, но влияет на значения, которые снимаются со счетчиков. Этот параметр называется масштабированием. По умолчанию мышь использует масштабирование 1:1. Однако хост может выбрать масштабирование 2:1, используя команду Set Scaling 2:1 (0xE7). Если будет включено масштабирование 2:1, то мышь применит следующий алгоритм к счетчикам перемещения прежде, чем отправить их содержание хосту (см. Таблицу 1).
Состояние счетчика движения | Отсылаемое значение |
---|---|
0 | 0 |
1 | 1 |
2 | 1 |
3 | 3 |
4 | 6 |
5 | 9 |
N>5 | 2 * N |
Масштабирование 2:1 применяется только к автоматически отсылаемым данным в потоковом режиме. Это не влияет на данные, которые отправляются в ответ на команду Read Data (0xEB).
Пакет данных о перемещениях
Стандартная мышь PS/2 посылает информацию о движении/кнопках хосту, используя следующий 3-хбайтовый пакет (см. Таблицу 2):
Байт | Бит 7 | Бит 6 | Бит 5 | Бит 4 | Бит 3 | Бит 2 | Бит 1 | Бит 0 |
---|---|---|---|---|---|---|---|---|
1 | Y-переполнение | X-переполнение | Y знак бит | Х знак бит | всегда 1 | Средняя кнопка | правая кнопка | левая кнопка |
2 | X-перемещение | |||||||
3 | Y-перемещение |
Значения перемещения – это два 9-разрядных целых числа, где старший значащий бит, появляется битом “X/Y знак” в 1-м байте из передаваемого пакета данных. Их значение представляет смещение мыши относительно ее предыдущей позиции, определенной текущим разрешением. Диапазон отсылаемых значений лежит в пределах от -255 до +255. Если этот диапазон будет превышен, то установится соответствующий бит переполнения.
Режимы работы
Создание отчетов данных обработано согласно режиму, в котором работает мышь. Есть четыре режима работы:
- сброс (Reset) – начальный режим, в котором мышь выполняет инициализацию и самодиагностику;
- поток (Stream) – рабочий режим по умолчанию, в котором мышь отправляет пакеты данных о перемещениях, когда происходит перемещение или изменяются состояния кнопок;
- удаленный (Remote) – хост должен упорядочить пакеты данных о перемещениях;
- обратный (Wrap) – диагностический режим, в котором мышь отправляет каждый полученный пакет назад к хосту.
Режим сброса (Reset)
Мышь переходит в режим сброса при включении питания или в ответ на команду Reset (0xFF). После ввода этого режима мышь выполняет диагностическую самопроверку и устанавливает следующие значения по умолчанию:
Частота дискретизации =100 значений в секунду
Разрешение =4 значения на 1мм
Масштабирование =1:1
Создание отчетов данных = отключено
После режима самодиагностики мышь отправляет хосту результат проверки – 0xAA (успешно) или 0xFC (ошибка).
Следующим значением после 0xAA или 0xFC мышь отправляет свой ID=0×00. Это значение указывает на то, что это мышь, а не клавиатура или иное устройство. В некоторых описаниях протокола указано, что хост не должен передавать данные, пока тот не получит ID устройства. Однако некоторые BIOS’ы отправляют команду Reset (0xFF) сразу после команды 0xAA, полученной после того, как включенное питание было сброшено.
Как только мышь отправила свой ID устройства хосту, она входит в потоковый режим.
Потоковый режим (Stream)
В потоковом режиме мышь отправляет данные перемещения, когда обнаруживается перемещение или изменение в состоянии одной или более кнопок мыши. Максимальная частота, на которой можно сообщить об этих данных, известна как частота дискретизации. Этот параметр изменяется в пределах от 10 до 200 значений в секунду (по умолчанию – 100 значений в секунду). Хост может изменить это значение, используя команду Set Sample Rate (0xF3).
Отметим, что создание отчетов отключено по умолчанию. Мышь не будет фактически слать пакеты данных о перемещениях, пока она не получит команду Enable Data Reporting (0xF4).
Потоковый режим – рабочий режим по умолчанию или режим, в который можно установить, используя команду Set Stream Mode (0xEA).
Удаленный режим (Remote)
В удаленном режиме мышь читает свои входы и обновляет счетчики/флаги на текущей частоте дискретизации, но не отсылает автоматически пакеты данных, если произошло перемещение. Вместо этого хост опрашивает мышь, используя команду Read Data (0xEB). После получения этой команды мышь отсылает единственный пакет данных о перемещениях и сбрасывает свои счетчики перемещения.
Мышь входит в удаленный режим при получении команды Set Remote Mode (0xF0). Удаленный режим используется редко.
Обратный режим (Wrap)
Это “эхо”-режим, в котором каждый байт, полученный мышью, отсылается назад к хосту. Даже если полученный байт будет соответствовать системной команде, то мышь не станет отвечать на эту команду, а только отправит данный байт назад хосту. Однако есть два исключения: команды Reset (0xFF) и Reset Wrap Mode (0xEC). Мышь обработает их как допустимые команды и не отправит назад хосту.
Обратный режим используется редко.
Расширения Intellimouse
Популярное расширение стандартной мыши PS/2 – Microsoft Intellimouse – включает в себя поддержку в общей сложности пяти кнопок мыши и трех осей перемещения (вправо/влево, вверх/вниз и колесо прокрутки). Эти дополнительные функции требуют использования 4-хбайтового пакета данных перемещения, а не стандартного 3-хбайтового. Так как стандартные драйверы мыши PS/2 не могут распознать этот пакетный формат, Intellimouse обязан работать точно как стандартная мышь PS/2. Таким образом, если Intellimouse будет использоваться на компьютере, который поддерживает только стандартную мышь PS/2, то она (мышка) будет все еще функционировать, за исключением колеса прокрутки и 4-й и 5-й кнопок.
После включения питания или сброса Microsoft Intellimouse работает точно так же, как стандартная мышь PS/2 (то есть использует 3-хбайтовый пакет данных о перемещениях, отвечает на все команды таким же образом, как на стандартную мышь PS/2, и сообщает о ID устройства 0×00).
Чтобы включить колесо прокрутки, хост отправляет следующую последовательность команд:
- Установить частоту дискретизации =200
- Установить частоту дискретизации =100
- Установить частоту дискретизации =80
Хост отправляет команду Get device ID (0xF2) и ожидает ответа. Если это стандартная мышь PS/2 (то есть не IntelliMouse), то она ответит ID устройства 0×00. В этом случае хост распознает факт, что мышь действительно не имеет колеса прокрутки, и будет продолжать обрабатывать ее как стандартную мышь PS/2. Однако если будет подключена Microsoft Intellimouse, то она ответит ID =0×03. Это сообщит хосту, что у присоединенного манипулятора есть колесо прокрутки, и хост перейдет в режим ожидания 4-хбайтового пакета данных о перемещениях (см. Таблицу 3):
Байт | Бит 7 | Бит 6 | Бит 5 | Бит 4 | Бит 3 | Бит 2 | Бит 1 | Бит 0 |
---|---|---|---|---|---|---|---|---|
1 | Y-переполнение | X-переполнение | Y знак бит | Х знак бит | всегда 1 | Средняя кнопка | правая кнопка | левая кнопка |
2 | X-перемещение | |||||||
3 | Y-перемещение | 4 | Z-перемещение |
“Z-перемещение” – дополнительное значение, которое представляет собой перемещение колеса прокрутки начиная с последнего отчета данных. Допустимые значения находятся в диапазоне от -8 до +7. Это означает, что число фактически представлено как четыре младших, значащих бита; старшие четыре бита – только как расширение знака.
Чтобы включить колесо прокрутки, а также 4-ю и 5-ю кнопки, хост отправляет следующую последовательность команд:
- установить частоту дискретизации =200
- установить частоту дискретизации =200
- установить частоту дискретизации =80
Хост шлет команду Get device ID (0xF2) и ожидает ответа. Microsoft Intellimouse отвечает ID устройства =0×04, затем использует следующий 4-хбайтовый пакет данных перемещения (см. Таблицу 5):
Байт | Бит 7 | Бит 6 | Бит 5 | Бит 4 | Бит 3 | Бит 2 | Бит 1 | Бит 0 |
---|---|---|---|---|---|---|---|---|
1 | Y-переполнение | X-переполнение | Y знак бит | Х знак бит | всегда 1 | Средняя кнопка | правая кнопка | левая кнопка |
2 | X-перемещение | |||||||
3 | Y-перемещение | |||||||
4 | 0 | 0 | 5-я кнопка | 4-я кнопка | Z-перемещение |
4-я кнопка =1, если нажата 4-я кнопка мыши, и 5-я кнопка =1, если нажата 5-я кнопка мыши. “Z-перемещение” представляет собой значение движения, которое произошло начиная с последнего отчета данных. Действительные значения изменяются в пределах от -8 до +7.
Набор команд
Следующее – набор команд; он является стандартом для мыши с интерфейсом PS/2. Если мышь находится в потоковом режиме, хост должен отключить создание отчетов данных (команда 0xF5) прежде, чем отправить какие-то команды.
- 0xFF (Reset) – мышь отвечает командой (0xFA) и входит в режим сброса.
- 0xFE (Resend) – хост отправляет эту команду всякий раз, когда он получает ошибочные данные от мыши. Мышь отвечает, посылая последний пакет, который она отправила хосту. Если мышь отвечает на команду Resend другим недопустимым пакетом, хост может или дать другую команду Resend, или сформировать команду Error (0xFC) и произвести сброс питания с мыши. Эта команда не буферизована, что означает, что Resend никогда не будет отправляться в ответ на команду Resend.
- 0xF6 (Set Defaults) – мышь отвечает командой (0xFA) и устанавливает следующие значения: частота дискретизации =100, разрешение =4 значения на 1 миллиметр, масштабирование =1:1, создание отчетов данных = отключено. Мышь сбрасывает все свои счетчики перемещения и входит в потоковый режим.
- 0xF5 (Disable Data Reporting) – мышь отвечает командой (0xFA), отключается создание отчетов данных и сбрасываются счетчики перемещения.
- 0xF4 (Enable Data Reporting) – мышь отвечает командой (0xFA), включается режим создания отчетов данных и сбрасываются счетчики перемещения. Эта команда может быть получена в тот момент, когда мышь находится в удаленном режиме, но будет выполнена, только если мышь переключится в потоковый режим.
- 0xF3 (Set Sample Rate) – мышь отвечает командой (0xFA) и ожидает один байт из хоста. Мышь сохраняет этот байт как новую частоту дискретизации. После получения частоты дискретизации мышь снова отвечает командой (0xFA) и сбрасывает свои счетчики перемещения. Допустимые частоты дискретизации – 10, 20, 40, 60, 80, 100 и 200 значений в секунду.
- 0xF2 (Get Device ID) – мышь отвечает командой (0xFA), сопровождая его ID устройства (0×00 для стандартной мыши PS/2). Мышь должна также сбросить свои счетчики перемещения.
- 0xF0 (Set Remote Mode) – мышь отвечает командой (0xFA), сбрасывает свои счетчики перемещения и входит в удаленный режим.
- 0xEE (Set Wrap Mode) – мышь отвечает командой (0xFA), сбрасывает свои счетчики перемещения и входит в эхо-режим.
- 0xEC (Reset Wrap Mode) – мышь отвечает командой (0xFA), сбрасывает свои счетчики перемещения и входит в режим, в котором она была до эхо-режима (в потоковый или удаленный).
- 0xEB (Read Data) – мышь отвечает командой (0xFA) и отправляет пакет данных перемещения. Это единственный способ считать данные в удаленном режиме. После того, как пакет данных был успешно отправлен, мышь сбрасывает свои счетчики перемещения.
- 0xEA (Set Stream Mode) – мышь отвечает командой (0xFA), сбрасывает свои счетчики перемещения и входит в потоковый режим.
- 0xE9 (Status Request) – мышь отвечает командой (0xFA), затем передается следующий 3-хбайтовый пакет состояния (см. Таблицу 6):
Таблица 5. Пакет состояния Байт Бит 7 Бит 6 Бит 5 Бит 4 Бит 3 Бит 2 Бит 1 Бит 0 1 Всегда 0 Режим включение масштабирование всегда 0 левая кнопка средняя кнопка правая кнопка 2 Разрешение 3 частота опроса Правая, средняя, левая кнопка =1, если соответствующая кнопка нажата; 0, если кнопка не нажата. Масштабирование =1, если масштабирование 2:1; 0, если масштабирование 1:1 (см. команды 0xE7 и 0xE6).
Включение =1, если включено создание отчетов данных; 0, если создание отчетов данных отключено (см. команды 0xF5 и 0xF4).
Режим =1, если удаленный режим включен; 0, если включен потоковый режим (см. команды 0xF0 и 0xEA).
- 0xE8 (Set Resolution) – мышь отвечает командой (0xFA), читает один байт из хоста и снова отвечает командой (0xFA), затем сбрасывает свои счетчики перемещения. Побайтовое чтение от хоста определяет разрешение следующим образом (см. Таблицу 7):
Таблица 6. Расшифровка данных, принятых с хоста Байт, считанный с хоста Разрешение 00 1 значение/мм 01 2 значения/мм 02 4 значения/мм 03 8 значений/мм - 0xE7 (Set Scaling 2:1) – мышь отвечает командой (0xFA), затем включает масштабирование 2:1.
- 0xE6 (Set Scaling 1:1) – мышь отвечает командой (0xFA), затем включает масштабирование 1:1.
Инициализация
Мышь PS/2 обычно обнаруживается/инициализируется, только когда компьютер загружается. Таким образом, мышь не может использоваться в горячем подключении; в результате приходится перезапускать свой компьютер всякий раз, когда устанавливается/удаляется мышь PS/2.
Начальное обнаружение мыши PS/2 происходит во время включения компьютера. Если мышь будет обнаружена, то BIOS позволит операционной системе конфигурировать/использовать мышь.
Иначе устанавливается запрет на передачу по шине мыши. Если загружать компьютер с присоединенной мышью, отключить мышь и повторно ее включить в то время, когда загружается Windows, ОС в состоянии обнаружить мышь и дать с ней работать.
Рассмотрим передачу данных между компьютером и стандартной мышью PS/2 во время процесса начальной загрузки (Листинг 1).
Мышь: AA Тестовый режим
Мышь: 00 Идентификатор мыши
Хост: FF Команда сброса
Мышь: FA Подтверждение
Мышь: AA Тестовый режим
Мышь: 00 Идентификатор мыши
Хост: FF Команда сброса
Мышь: FA Подтверждение
Мышь: AA Тестовый режим
Мышь: 00 Идентификатор мыши
Хост: FF Команда сброса
Мышь: FA Подтверждение
Мышь: AA Тестовый режим
Мышь: 00 Идентификатор мыши
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: C8 число 200
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 64 число 100
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 50 число 80
Мышь: FA Подтверждение
Хост: F2 Прочитать тип устройства
Мышь: FA Подтверждение
Мышь: 00 Идентификатор мыши
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 0A число 10
Мышь: FA Подтверждение
Хост: F2 Прочитать тип устройства
Мышь: FA Подтверждение
Мышь: 00 Идентификатор мыши
Хост: E8 Установить разрешение
Мышь: FA Подтверждение
Хост: 03 8 значений/мм
Мышь: FA Подтверждение
Хост: E6 Установить масштабирование 1:1
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 28 число 40
Мышь: FA Подтверждение
Хост: F4 Включить
Мышь: FA Подтверждение
Инициализация завершена…
Если нажата левая кнопка.
Мышь: 09 1 1 00001001; Бит0 – состояние левой кнопки мыши; Бит3 всегда =1
Мышь: 00 1 1 Нет X-перемещений
Мышь: 00 1 1 Нет Y-перемещений
… и отпущена левая кнопка мыши:
Мышь: 08 0 1 00001000 Бит0 – состояние левой кнопки мыши; Бит3 всегда =1
Мышь: 00 1 1 Нет X-перемещений
Мышь: 00 1 1 Нет Y-перемещений
Теперь рассмотрим обмен данными между компьютером и мышью Intellimouse (при загрузке компьютера).
Мышь: AA Тестовый режим
Мышь: 00 Идентификатор мыши
Хост: FF Команда сброса
Мышь: FA Подтверждение
Мышь: AA Тестовый режим
Мышь: 00 Идентификатор мыши
Хост: FF Команда сброса (все, надоело. Дальше хост не выделяю)
Мышь: FA Подтверждение
Мышь: AA Тестовый режим
Мышь: 00 Идентификатор мыши
Хост: FF Команда сброса
Мышь: FA Подтверждение
Мышь: AA Тестовый режим
Мышь: 00 Идентификатор мыши
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: C8 число 200
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 64 число 100
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 50 число 80
Мышь: FA Подтверждение
Хост: F2 Прочитать тип устройства
Мышь: FA Подтверждение
Мышь: 03 Идентификатор мыши
Хост: E8 Установить разрешение
Мышь: FA Подтверждение
Хост: 03 8 значений/мм
Мышь: FA Подтверждение
Хост: E6 Установить масштабирование 1:1
Dev: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 28 число 40
Мышь: FA Подтверждение
Хост: F4 Включить устройство
Мышь: FA Подтверждение
Если нажата левая кнопка мыши…
Мышь: 09 00001001 Бит0 – состояние левой кнопки мыши; Бит3 всегда =1
Мышь: 00 Нет X-перемещений
Мышь: 00 Нет Y-перемещений
Мышь: 00 Нет Z-перемещений
… и отпущена левая кнопка мыши:
Мышь: 08 00001000
Мышь: 00 Нет X-перемещений
Мышь: 00 Нет Y-перемещений
Мышь: 00 Нет Z-перемещений
После установки драйвера IntelliMouse Microsoft с поддержкой 4-х и 5-х кнопок была найдена следующая последовательность:
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: C8 число 200
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 64 число 100
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 50 число 80
Мышь: FA Подтверждение
Хост: F2 Прочитать тип устройства
Мышь: FA Подтверждение
Мышь: 03 Идентификатор мыши
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: C8 число 200
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: C8 число 200
Мышь: FA Подтверждение
Хост: F3 Установить частоту дискретизации
Мышь: FA Подтверждение
Хост: 50 число 80
Мышь: FA Подтверждение
Хост: F2 Прочитать тип устройства
Мышь: FA Подтверждение
Мышь: 04 Идентификатор мыши
….
Заключение
Удалось выяснить принцип работы мышки – протокол обмена с компьютером. Благодаря полученной информации представилась возможность использовать мышь не только по ее прямому назначению, но и для поделок, как я говорил выше. Самый простой пример – это прикрепление мышки под днище самодельного робота, а так как мы знаем протокол обмена, можно, например, сделать дешевый импровизированный GPS-навигатор, и ваш робот будет всегда знать, где он находится, а заодно и шарики мышки будут использоваться как дополнительные колеса. Другой пример. Подключаем мышку к тому же самому роботу и управляем им: двинул мышь вперед – робот поехал вперед, кликнул мышкой – робот выстрелил импровизированной ракетой.
Ресурсы
Статья в Википедии
http://ru.wikipedia.org/wiki/Компьютерная_мышь
Мышь, которая всегда под рукой. Веб-архив журнала “Наука и жизнь”
http://www.nkj.ru/archive/articles/8810
Принцип работы компьютерной мыши
http://www.molodinfo.n-vartovsk.ru/insite/mouse/mouse_tech.htm
The PS2 protocol
http://pcbheaven.com/wikipages/The_PS2_protocol
Подключение PC AT клавиатуры к AVR
http://radioded.ru/content/view/53/49
Описание протокола PS/2 для мыши и клавиатуры
http://marsohod.org/index.php/ourblog/11-blog/57-ps2proto
Adam Chapweske. The PS/2 Mouse/Keyboard Protocol
http://www.computer-engineering.org/ps2protocol
Adam Chapweske. The PS/2 Mouse Interface
http://www.computer-engineering.org/ps2mouse
Статья из шестого выпуска журнала “ПРОграммист”.
13th
Сен
Шестой выпуск журнала «ПРОграммист»
От редактора. Здравствуйте, уважаемые читатели журнала «
В этом выпуске…
Материал про юзабилити интерфейсов продолжит Александр Демьяненко в рубрике «Отдел тестирования». И наконец-то стартует рубрика «Переводные материалы». Статья Евгения Амосова подробно расскажет нам про протокол работы наших хвостатых PS/2-помощников.
Что такое ТЗ? Как правильно составить ТЗ? Кто должен составлять ТЗ? Прямо голова идет кругом. Но не расстраивайтесь. На эти и другие вопросы постарается ответить Дарья Устюгова.
Для развития координации, цветового восприятия ребенку до 3-5 лет требуются простые и понятные игрушки. Одним из ярких примеров является раскраска и интерактивный алфавит. Не мне вам рассказывать, родители меня поймут, что увлечь малыша могут веселые и красочные картинки зверей, игрушек. Мало того, возможность почувствовать себя художником – это непередаваемое ощущение для ребенка. А поможет нам в этом ваш верный помощник – компьютер и Владимир Дегтярь в рубрике «2D графика». Оценивать же будет самый строгий и требовательный судья – ребенок, экзаменатор нашего труда.
Хорошо-ли вы умеете готовить? Нет, это не шутка , вспомните про направленность нашего журнала. Вспомнили? Cегодня в рубрике «Лаборатория» шеф-повар, Виталий Белик, приготовит компилятор по своему домашнему рецепту. Bon Appetit, друзья!
В рубрике «Архив» мы решили поднять тему взаимодействия с MySQL в C++. В данной статье делится опытом наш форумчанин psycho-coder.
Вот и настал час 0xFF! Мы знаем, что среди наших читателей есть не только программисты, сисопы, но и биологи, радиоинженера и радиолюбители, дизайнеры, физики и химики, профессионалы и просто обычные люди С этим стоит немножко поспорить. Не совсем обычные. Ведь практически сейчас все, что нас окружает, то чем мы пользуемся ежедневно и ежечасно, так или иначе связано с программированием. Пожалуй, ни одна профессия не пустила так глубоко свои корни во все сферы нашей жизни, как
СОДЕРЖАНИЕ НОМЕРА
ТЕМА НОМЕРА
Наши разработки …………………………….. ……….. с.0х02
НЕВЕРОЯТНО, НО ФАКТ
Любопытные факты …………………………….. ………. с.0х03
ПЕРЕВОДНЫЕ МАТЕРИАЛЫ
Исследование протокола PS/2 для мышки …………………… с.0×06
ОТДЕЛ ТЕСТИРОВАНИЯ
Разработчики – интерфейс – пользователи. Часть 2 …………. с.0х0E
ОБЩИЕ ВОПРОСЫ
О правильном составлении ТЗ. Часть 1 ……………………. с.0х14
2D ГРАФИКА
Программы для самых маленьких ………………………….. с.0х17
ЛАБОРАТОРИЯ
Компилятор домашнего приготовления ……………………… с.0х1D
АРХИВ
Работа с MySQL в С++ …………………………….. …… с.0х2A
ЮМОР
Афоризмы – хохмы – загадки …………………………….. с.0х32
Ссылки для скачивания (формат PDF, 53 c., в журнале исходники к статьям):
http://slil.ru/29668837
http://raxp.radioliga.com/cnt/s.php?p=pro6.pdf
http://programmersclub.ru/pro/
http://procoder.info
30th
Авг
? Выбираем логотип журнала ПРОграммист ?
Прикрепляем к сообщению свои варианты. Имейте совесть, не грузите людей простынёй из 50 вариантов
1 человек – 1 пост / 1 лого – 1 файл.
В пост влазит 5 файлов, соответственно каждый может предложить 5 вариантов.
Варианты, естественно, не окончательные, будут дорабатываться до человеческого состояния. Потом прилепим голосование (возможно в 2 тура)
Обсуждение здесь
27th
Авг
Искусство изменеия GTA
Здравствуйте, любители гейминга. В данной статье, я хочу показать, как делаются плагины для всем известной GTA. Начнем мы самого простого – это программирование плагинов на Delphi для Grand theft Auto ViceCity. А поняв принцип их работы, никакого труда не составит написать плагин и для других серий GTA…
Виталий Иванов
by VintProg vintprog@gmail.com
Пишем простой плагин для GTA – VC*
Итак, Для работы нам понадобится следующее:
- IDE среда Delphi [1]
- знание языка
- утилита ArtMoney** [2]
И немного теории, что же такое плагины. Плагины – это те же динамические подключаемые библиотеки DLL. Однако часто бывает, что им изменяют расширение.
Возможно, вы спросите: «…как же это работает все?», А работает оно следующим образом… При запуске <gta-sa.exe> загружаются комплектные библиотеки от разработчиков. В одной из этихDLL, в частности — vorbisFile.dll имеется функция загрузки библиотек *.asi, И пожелавшие остаться неизвестными, программисты написали <cleo.asi> и набили ее весьма и весьма полезными функциями, такими как: новые опкоды, загрузка плагинов *.cleo и.т.п. Когда загрузилась библиотека <cleo.asi>, ее код выполняет нужные функции в памяти игры.
* Комментарий автора.
Вы наверняка встречали Cleo на GTA-SA,и видели, что там существует такая библиотека cleo.asi, Так вот она и загружает из папки Cleo – скрипты и сами плагины .cleo.
Именно благодаря этому и появляются новые возможности в игре. А что касается GTA Vice City, то в ней тот же процесс, только библиотеки *.asi загружаются из библиотеки <Mss32.dll>. Отсюда понятно, что для того чтобы писать плагины – необходимо хорошо уметь работать с памятью игры и знать что за значения находятся в игровой памяти в определенном адресе.
** Комментарий редакции.
ArtMoney – программа, предназначенная для редактирования параметров в кмпьютерных играх, для получения бесконечных денег, жизней, патронов и т.п. Она умеет сканировать память или файлы игры для поиска каких-то определенных значений (деньги, ресурсы). Официальный сайт www.artmoney.ru
Приступим… Для начала запустим Borland Delphi, после чего кликаем на «File -> New -> other… » и перед нами откроется вот такое окно (см. рисунок 2):
Рис. 2. Выбор DLL Wizard
Далее выделяем DLL Wizard и жмем OK. Сразу возьмем и сохраним наш проект «File->Save Project As» и под именем ShowMessage, чтобы получилось как показано на рисунке 3:
Рис. 3. Заготовка плагина
Также можно удалить дерективу {$R *.res} , потому-что для данного плагина мы не будем использовать ресурсы. Из дополнительных модулей оставим лишь – Windows, а остальные удалим. Теперь напишем следующий код:
код:
uses
Windows;
var HWND : THandle;
begin
// Получить хендл окна GTA: Vice City
HWND := FindWindow(nil,’GTA: Vice City’);
if HWND <> 0 then // Проверка если окно GTA: Vice City
// существует, то тогда выполнить MessageBox
MessageBoxA(HWND,’Плагин загружен’,’Сообщение’,0)
end.
И скомпилируем его. Прокомментирую работу данного кода… Когда библиотека загружается, между Begin end начинает выполнятся код, Тут сразу появляется окошко с сообщением. Чтобы это заработало, переименуйте расширение *.DLL на *.ASI или воспользуйтесь директивой {$E.ASI}. После чего, скопируйте библиотеку в каталог GTA и запустите игру GTA-VC.exe, Далее мы увидим окошко, когда загрузится <mss32.dll>. Поздравляю, вы написали свой первый плагин для GTA***!
Пишем простой плагин-трейнер для GTA – VC
Настала пора сделать что-то полезное. Итак, сперва подумаем, что нам еще нужно? А нужно нам сделать плагин-трейнер. Для чего он предназначен? К примеру, мы можем сделать так, чтобы при нажатии определенной кнопки в игре – появлялось окно, в котором можно прибавить деньги игроку. Для этого, запускаем Gta-VC, сворачиваем ее и запускаем ArtMoney. В этой утилите выбираем «Искать -> Объект -> Процесс» и в выпадающем списке, где написано «Выбери процесс», выберем нашу GTA Vice City (см. рисунок 4). Теперь зайдем в GTA-VC и соберем небольшое количество денежных средств (к примеру, как я набрал $18). Судя по представлению «денег» в игре, видно, что они целого числа и можно набрать их в игре большое количество. Отсюда, мы уже знаем тип представления данных «денег».
Это 4-байтные целые числа, которые нам и нужно отыскать (см. рисунок 5).
Рис. 4. Выбор GTA-VC в ArtMoney
Обратите внимание! Вам необходимо повторить те же действия, как показано на рисунке, только у вас будет свое число. По завершению поиска у вас появится длиннющий список адресов (см. рисунок 6). Но не переживайте по этому поводу. Ведь адрес «денег» мы будем искать более легким методом, На то она и ArtMoney. Следующий шаг будет таков: снова заходим в GTA и либо тратим, либо добавляем деньги к игроку Tommy, запоминаем измененное количество «денег» и сворачиваем GTA. Вписываем новое значение (см. рисунок 7):
*** Комментарий автора.
Если что то не получается то пример расположен в каталоге “Examples\Plugin1”.
Рис. 5. Поиск значений по заданному типу данных
Рис. 6. Выборка адресов в ArtMoney
Рис. 7. Повторный поиск значений по заданному типу данных
Теперь нажимаем «отсеять». Как видите, длина списка адресов значительно уменьшится. И так продолжаем до тех пор, пока не останется один адрес. Если-же все равно остается насколько адресов, то меняем в них значения и проверяем изменилось-ли количество «денег» в GTA (см. рисунок 8). Если все нормально, то нормально. Однако, еще осталось сделать одну проверку на указатель. ArtMoney не закрываем, так все и оставляем. Вырубаем GTA-VC и заново запускаем GTA-VC. В ArtMoney, в выпадающем списке, где написано «Выбери процесс», заново выбираем GTA и повторим операции по изменению значений по адресу местонахождения «денег». Если все работает нормально, то записываем этот адрес в текстовой блокнот и вырубаем GTAVC и выключаем ArtMoney. Теперь он нам не понадобится.
Итак, первый шаг сделан. Мы нашли адрес «денег» и остается лишь написать плагин.
Распишем особенность работы плагина, то есть то – как он будет работать: так, при нажатии кнопки <M> добавляется 1000 долларов, а значит нам нужен обработчик нажатия кнопки. Воспользуемся таймером. Теперь, точно так же как и в первом примере, созадим новый проект DLL и назовем его «MoneyAdds». И напишем следующий код:
**** Комментарий автора.
Хочу напомнить, что не на всех версиях GTA-VC могут быть одинаковые адреса. Так что имейте это ввиду, при написании плагинов.
код:
{ GTA-VC 1.1 Плагин для добовления денег }
uses Windows;
type // определяем свой тип (указатель целых чисел)
P_Integer = ^Integer;
var
GTA_VC_Handle : THandle;
CurrentMoney : Integer;
keyUp : boolean;
const // тут твой найденный адрес «денег»
Address_Money = $0094ADD0;
{$E .asi}
//—- Эта процедура будет вызываться таймером —
procedure Timer_begin;
begin
// Нажатие и отпуск «M»
if not GetKeyState($4D) < 0 then keyUp := true;
if (GetKeyState($4D) < 0) and (keyUp = true) then
begin
// Читаем деньги из GTA-VC и присваиваем в CurrentMoney
CurrentMoney := P_Integer(Address_Money)^;
// Записываем 1000 + текущие деньги
P_Integer(Address_Money)^ := CurrentMoney + 1000;
keyUp := false;
end
end;
//————————————————————
begin
GTA_VC_Handle := FindWindow(nil,’GTA: Vice City’);
if GTA_VC_Handle <> 0 then
begin
SetTimer(GTA_VC_Handle,0,25,@Timer_begin);
end;
end.
Исходный код этого плагина находится в «Examples\Trainer1» [3]. Вот мы и реализовали плагин-трейнер добавления «денег» по нажатию клавиши «M», Только скажу – не выгодно на каждый плагин делать один таймер, Поэтому имейте ввиду, что таймер нагружает процессор. Для решений данной проблемы можно воспользоваться функциями DirectX для обработки нажатий клавиатуры.
Пишем свой собственный менеджер-загрузчик плагинов
Вы наверняка заметили одну вещь: когда создаешь новый плагин, его приходиться бросать рядом с GTA-VC.exe, А представьте себе, если таких плагинов будет больше десятка? Это не наш метод, поэтому мы напишем свой загрузчик плагинов из отдельно созданного каталога под наши плагины, скажем <bin>. И пускай там будет их хоть 1000!
Итак, запускаем среду Delphi и по аналогии создадим проект DLL-ки. Внутри напишем следующий код:
код:
{ Даная библиотека нужна для загрузки плагинов в GTA-VC }
{$E .ASI}
uses SysUtils, Windows;
var
SearchRec : TSearchRec;
filename : pAnsiChar;
const
dir_bin = ‘Bin\*.bin’;
dir_dll = ‘Bin\*.dll’;
//— Процедура отыскивает все плагины из папки Bin и
// подгружает их —
procedure Load_libs(FilesName : string);
begin
if FindFirst(FilesName, faAnyFile, SearchRec) = 0 then
repeat
filename := pAnsiChar(‘Bin\’ + SearchRec.name);
LoadLibrary(filename);
until FindNext(SearchRec) <> 0;
end;
//————————————————————
begin
Load_libs(dir_dll);
Load_libs(dir_bin);
end.
Исходный код этого менеджера-загрузчика находится в «Examples\Loader_VC» [3].
Заключение
Вот теперь готов загрузчик плагинов bin и dll, Теперь достаточно бросить его в корневую папку GTA, создать там каталог <BIN> со всеми нашими плагинами, запустить игру и полюбоваться результатом наших трудов.
Рассматриваемые в данной статье исходники плагина добавления «денег», плагина-трейнера и менеджера-загрузчика полностью приведены в виде ресурсов в теме «Журнал клуба программистов. Пятый выпуск» или непосредственно в архиве с журналом.
Продолжение смотрите в следующем выпуске нашего журнала…
Ресурсы
- Бесплатный TurboDelphi-Lite (over BDS-2006) http://www.andyaska.com/?act=download&mode=get&id=34
- Скачать ArtMoney http://www.artmoney.ru/r_download_se.htm
- Учебник. Искусство изменения GTA http://programmersup.3dn.ru/load/skachat_vse_na_gta/uchebniki_po_gta/10
- Модули и проекты, использованные в статье http://programmersclub.ru/pro/pro5.pdf
Статья из пятого выпуска журнала «ПРОграммист».
Скачать этот номер можно по ссылке.
Ознакомиться со всеми номерами журнала.
26th
Авг
Энкодер датчика pdf на плис. Часть 2
Итак, сегодня мы с вами продолжим наш материал по промышленной автоматизации, рассмотрим реализацию алгоритма энкодера на ПЛИС, особенности прошивки модулей Faswell UNIOxx-5 и создадим тестовую утилиту визуализации состояния шифратора приращений.
Продолжение. Начало цикла смотрите в четвертом выпуске журнала…
Сергей Бадло
by raxp http://raxp.radioliga.com
Согласно требований из прошлого материала [1], приведем реализацию алгоритма энкодера на ПЛИС в среде Xilinx Foundation 3.1i [2] (см. рисунки 2…8).
Каждая микросхема FPGA обрабатывает сигналы от двух датчиков. По каждому четвертому сигналу чтения READ и нулевому адресу (битовая последовательность А0…А2) передаются: младшие 8- бит 11-ти разрядной последовательности, содержащей информацию о текущем количес- тве импульсов поступивших с канала А (угловом положении). По первому адресу (А0…А2) переда- ются (с младшего по старший бит DO0…DO7): старшие 3- бита 11-ти разрядной информации о текущем количес- тве импульсов поступивших с канала А, признак отказа канала А (в течении длитель- ности стробирую- щего импульса (одного оборота), признак отказа канала В (в течении длитель ности стробирую- щего импульса (одного оборота)), признак направления вращения (определяется с началом стробирующего импульса по приходу сигналов с канала А и В (в течении одного оборота)), признак отказа канала строба О. По второму и третьему адресу (А0…А2) передаются (с младшего по старший бит DO0…DO7) 16- бит информации о количестве оборотов. По четвертому и пятому адресу (А0…А2) передаются (с младшего по старший бит DO0…DO7) 16- бит информации о количестве ТИ за длительность строба (относительной* скорости).
Рис. 1. Тестируемый пациент
Рис. 2. Общая схема энкодера (дешифратор адреса, формирователь кода схемы, генератор ТИ, 2 канала энкодера, 16 каналов дискретного ввода-вывода)
Рис. 3. Схема одного канала энкодера (антидребезг, задатчик идентификатора схемы, реверсивный счетчик импульсов, узел формирования выходного пакета данных)
Каждая из схем энкодера (алгоритм) осуществляет:
- измерение относительного углового положения датчика (по количеству импульсов поступивших с канала A);
- индикацию в соответствующем бите признака наличия импульсов (исправность A, B, Q);
- подсчет числа оборотов (количества импульсов строба), максимальное ограничено величиной 215, далее осуществляется сброс, и счет начинается заново;
- измерение относительной скорости*, т.е. подсчета количества ТИ, поступивших за длительность строба (погрешность ограничена длительностью ТИ и нестабильностью тактового генератора).
Рис. 4. Модуль задержки на 8 тактов встроенного генератора
Информация о текущей скорости определяется сразу же по истечении импульса строба, т.е. после первого оборота и далее по каждому последующему стробу.
где: 4 – коэффициент, учитывающий соотношение длительности строба и длительности канальных (информационных импульсов); 2048 – максимальное количество импульсов по любому из каналов; V – фактическая скорость, об/cек; Nти – количество ТИ за строб; Tти – период ТИ, поступающих с внешнего генератора (равен 20 нс), сек.
Рис. 5. Схема определения направлени вращения ротора датчика PDF
Рис. 6. Схемы нарезки сигналов A/B
Рис. 7. Схема регулирования (СР) ширины ЗИ
Рис. 8. Внутренняя схема узла адресации СР
Рис. 9. Реализация RS-триггера
Запустим наш проект на компиляцию. После проверки схемы, среда создаст битовый файл прошивки (файлы bitstream фирмы XILINX), который нужно прошить в соответствующую матрицу FPGA. Как это сделать?
Программирование платы UNIOхх-5
Обычно, для загрузки конфигурационной информации в ПЛИС используется JTAG интерфейс [3]. При этом последовательность действий следующая. Откройте приложение «JTAG Programmer» пакета Xilinx Foundation Series 3.1i. В меню «Edit» выберите пункт «Add Device», после чего появиться вспомогательное окно, в котором необходимо отыскать наш файл прошивки и открыть его. В меню «Operations» выберите пункт «Program». В появившемся окне нажмите кнопку «OK». После удачной загрузки, должна появиться фраза «Programming completed successfully». На этом программирование завершено (см. рисунок 10):
Рис. 10. Загрузка прошивки через JTAG
Однако этот способ не лишен недостатков: требуется наличие JTAG программатора практически все время «под рукой».
Второй способ, получивший название ISP (In System Programming), более удобен, так как не требует извлечения микросхемы конфигурационного ПЗУ (или самой ПЛИС) из платы или наличия дополнительного разъема на плате под ПЛИС. Программирование каждой матрицы FPGA1…FPGA4 модуля UNIOxx-5 осуществляется с помощью программ: <isp.exe> (загрузка схемы с записью в EEPROM), <isl.exe> (загрузка схемы без записи в EEPROM). Программы позволяют осуществить загрузку файлов схем *.bit матриц FPGA. Рассмотрим на примере…
Для записи схемы и загрузки в модуль необходимо запустить программу <isp.exe> с указанием Базового Адреса (Hex) модуля и кодов схем матриц FPGA, например (см. рисунок 11):
Рис. 11. Результат
Рис. 11. Окно командной строки. Запуск утилиты ISP
Питание и установка
Модуль с прошивкой энкодера устанавливается в слот (ISA) промышленного компьютера и запитан от напряжения ±5В. При этом требуется обеспечить стабильность питающего напряжения с пульсациями не более 200 мВ. Выходной сигнал – однополярное напряжение (цифровая последовательность) с током нагрузки до 10 мА. Максимальная длина шлейфа, соединяющая выход модуля энкодера (платы UNIOxx-5) к входному разъему до 1.5 м. Проверка прошитой платы UNIOxx-5 производится на рабочем месте следующим образом. Земли шасси, платы UNIOxx-5, осциллографа, генератора импульсов и генератора сигналов высокочастотного должны быть объединены (заземлены) в одной точке проводником минимальной длины сечением не менее 2 мм2 (см. рисунок 12):
Рис. 12. Схема проверки платы UNIOxx-5 с прошивкой энкодера
Плата UNIOxx-5 при выключенном питании шасси вставляется в ISA слот. С помощью соединительного шлейфа присоединяют выход датчиков с соответствующими входами модулей гальванической развязки или модулей связи, сигнал с которых подан на вход платы UNIOxx-5. Далее производится включение промышленного шасси и загрузка обслуживающей прошивки. Осциллографом контролируют наличие напряжения питания (+5 В) на плате модуля UNIOxx-5. С помощью осциллографа производится контроль ТИ с тактового генератора платы. Включив генератор импульсов с заранее выставленными выходными уровнями сигналов, подают импульсы по входу строба для каждой из матрицы модуля (поочередно), контролируя одновременно с помощью программы обслуживания (или осциллографом) наличие признака строба в последовательности (бит DO7 при втором адресе А0…А2).
Кроме того, тестирование прошивки энкодера желательно провести в условиях, приближенных к «реальным». Для этого необходимо, подключить шифратор приращений через наш модуль связи, используя несколько десятков метров кабеля UTP 5-й категории обмотанного вокруг работающего частотного привода под нагрузкой. Для визуализации положения ротора и подсчета количества импульсов с датчика приращений, реализуем на языке С простейшую утилиту приема данных с платы UNIO по шине ISA…
Реализация тестовой утилиты приема данных с энкодера и визуализация
Прежде всего, на основе схемы (алгоритма) энкодера составим таблицу распределения его выходных сигналов (см. таблицу). Для упрощения написания программы можно воспользоваться библиотечными функциями языка С. В качестве примера рассмотрим фрагмент программы, осуществляющей тестовое считывание данных с контроллера и вывод графического отображения положения ротора датчика PDF. В начале программы необходимо определить адрес считывания, согласно таблице (см. листинг 1):
Таблица. Расшифровка выходных сигналов энкодера
код:
…
int f_dba(int dba,int d[16])
{
int i,PA;
for (i=0;i<16;i++) { PA=inp(BA+dba+i); d[i]=PA; } } // dba- адрес FPGA, d- данные
Адрес считывания по ISA = BA+BAx+Ar, где BA – базовый адрес платы UNIOxx (выставлен 0x110), BAx – базовый адрес матрицы FPGA:
- для FPGA-1 (BAx=0xA000)
- для FPGA-2 (BAx=0xA400)
- для FPGA-3 (BAx=0xA800)
- для FPGA-4 (BAx=0xAC00)
Рассмотрим подробнее…
Запустим среду Turbo C++ IDE ver.3.0 от Borland и создадим пустой проект. Далее необходимо провести инициализацию графического режима через InitGraph() и зациклить опрос порта INP(). Причем предусмотреть выход из цикла по нажатию определенной комбинации клавиш во время опроса, к примеру, использованием функций Kbhit() и Getch(). В основном цикле необходимо осуществить считывание двухбайтного кода идентификатора схемы, положения ротора, признаков отказа и направления вращения. После чего, произвести отображение выделенных параметров в графическом виде. Оптимальным с точки зрения наблюдения – является имитация положения ротора датчика приращений с выводом дополнительной служебной информации. Реализация подобного подхода представлена в листинге 2:
код:
…
#include «dos.h»
#include «string.h»
#include
#include
#include
#include
#define BA 0xA110
#define pi 3.14159
int f_dba(int dba,int d[16]);
void main(long int argc, char *argv[])
{
// переменные порта
int dba;int d[16];
// переменные данных
int FL,ugc,ugc2,FL1,FL2;
// служебные переменные
int s,pd,i,nomer,d1,d2;
long int k,KT,KT2;
float ugol,ugol2,ug,n,ug2,ug1,KTT,vm,ug_1,ug_2;
double v,ng1,ng2,nn;
int gdriver = DETECT, gmode, errorcode;
char msg[80];
int x,x2,y,y2,ns,nc,ugg,ugg2,xr,xr2,yr,yr2,wr,wr2,hr,hr2;
char nbuf[25];
char kbuf[25];
char ubuf[25];
char ibuf[25];
char sbuf[25];
char pbuf[25];
clrscr();
if(argc>1) // проверка параметров командной строки
{
printf(«argc=%d\n»,argc);
for(i=0;i
{ // выдаем описание
printf(«Program driver encoder\n»);
}else if (strcmp(argv[1],»indtest») == 0){}
}
//
clrscr();
k = 1;
puts(«Расчитывать параметры по FPGA-?»);
puts(«Количество импульсов с датчика PDF-?»);
scanf(«%d %d», &s, &pd); // считываем номер FPGA
if (s == 1) {dba = 0x0;} // задаем базовый адрес
if (s == 2) {dba = 0x400;}
if (s == 3) {dba = 0x800;}
if (s == 4) {dba = 0xC00;}
/*Инициализация графического режима*/
initgraph(&gdriver, &gmode, «»);
errorcode = graphresult();
if (errorcode != grOk)
{
printf(«Graphics error: %s\n», grapherrormsg(errorcode));
printf(«Press any key to halt:»);
getch();
}
/*Параметры рисунка*/
xr=180; // положение 1-го графика
yr=250;
xr2=470; // положение 2-го графика
yr2=250;
wr=100; // размеры 1-го графика
hr=100;
wr2=wr; // размеры 2-го графика
hr2=hr;
//
setfillstyle(1,BLUE);
fillellipse(xr, yr, wr, hr);
fillellipse(xr2, yr2, wr2, hr2); // 2-канал
setcolor(GREEN);
outtextxy(0,10, «Count:»); // количество импульсов
outtextxy(0,30, «Angle, deg:»); // угол поворота ротора
outtextxy(0,41, «Speed, r/s:»); // скорость
outtextxy(0,52, «HB:»); // направление вращения
outtextxy(0,63, «delta:»);
// градусы
outtextxy(xr+wr+15,yr-5,»0″);
outtextxy(xr2+wr2+15,yr2-5,»0″);
outtextxy(xr-wr-40,yr-5,»180″);
outtextxy(xr2-wr2-40,yr2-5,»180″);
outtextxy(xr-5,yr-hr-25,»90″);
outtextxy(xr2-5,yr2-hr2-25,»90″);
outtextxy(xr-10,yr+hr+15,»270″);
outtextxy(xr2-10,yr2+hr2+15,»270″);
do {
f_dba(dba,d);
printf(«Опрос:%d», k);
if (k>1)
{
setcolor(BLACK);
outtextxy(90,10, kbuf);
}
setcolor(RED);
itoa(k, kbuf, 10); // конвертируем в текст (max =10 символов)
outtextxy(90,10, kbuf);
/*FPGA_1*/
printf(«Идентификатор схемы= %c», d[14]); // см. схему
алгоритма на ПЛИС
//
n = ((d[3]<<8)|d[2]);
if (n < 0) {nn = 65535 + n - 32768;}
if (n > 0) {nn = n;}
printf(«Оборот: %7.1f», n);
if (k>1)
{
setcolor(BLACK);
outtextxy(5,90,nbuf);
}
nn=165001;
setcolor(RED);
itoa(nn,nbuf,25);
outtextxy(5,90,nbuf);
// формируем из двух байт значение положения (см. схему)
KT = ((d[1]<<8)|d[0]);
KT2 = ((d[6]<<8)|d[5]);
ugol = KT;
ugol2 = KT2;
// пересчитываем в угловые коорд.положение ротора-1 и ротора-2
ug = ugol*360/pd;
ug2 = ugol2*360/pd;
ugc = ug/360;
ugc2 = ug2/360;
ug = 360*(ugc-(ug/360));
ug2 = 360*(ugc2-(ug2/360));
ugg = 90-ug;
ugg2 = 90-ug2;
if (k>1)
{
setcolor(BLACK);
outtextxy(90,32, ubuf);
outtextxy(130,32, ibuf);
}
setcolor(RED);
itoa(ug,ubuf,10);
itoa(ugol,ibuf,10);
outtextxy(90,32,ubuf);
outtextxy(130,32,ibuf);
//
FL=d[4]&0x04; // определяем направление вращения
if (k>1)
{
if (FL == 0x04) {setcolor(BLACK);outtextxy(90,53,»left»);}
else {setcolor(BLACK);outtextxy(90,53,»rigth»);}
}
if (FL==0x04)
{
setcolor(BLACK);outtextxy(90,53,»rigth»);
setcolor(RED);outtextxy(90,53,»left»);
}
else
{
setcolor(BLACK);outtextxy(90,53,»left»);
setcolor(RED);outtextxy(90,53,»rigth»);
}
// значение разницы
if (k>1)
{
setcolor(BLACK);
outtextxy(90,63,pbuf);
}
setcolor(RED);
if (k == 1) {d1 = ug;d2 = ug2;}
itoa((ug-ug2)*pd/360,pbuf,10);
outtextxy(90,63, pbuf);
// скорость вращения
if (k==1) {ug_1=ug;ng1=ugc;}
ug_2=ug;ng2=ugc;
vm = ((ng2-ng1)*360-ug_1+ug_2)/(360*(125e-3));
printf(«Vm, об/c= %8.6f\n»,vm);
if (k>1)
{
setcolor(BLACK);
outtextxy(90,43,sbuf);
}
setcolor(RED);
itoa(vm,sbuf,10);
outtextxy(90,43,sbuf);
ug1=ug2; ng1=ng2;
// маркер (вектор на графике)
if (k>1)
{
setcolor(BLUE); // 1-канал
line(x,y,xr,yr);
line(x2,y2,xr2,yr2);
}
setcolor(RED);
line(xr-wr-10,yr,xr+wr+10,yr); // ось-x
line(xr2-wr2-10,yr2,xr2+wr2+10,yr2); // ось-x 2-канал
line(xr,yr+hr+10,xr,yr-hr-10); // ось-y
line(xr2,yr2+hr2+10,xr2,yr2-hr2-10); // ось-y 2-канал
x=xr+(wr*sin(ugg*pi/180)); x2=xr2+(wr2*sin(ugg2*pi/180));
y=yr+(hr*cos(ugg*pi/180)); y2=yr2+(hr2*cos(ugg2*pi/180));
setcolor(RED); // 1-канал
line(xr,yr,x,y);
line(xr2,yr2,x2,y2);
// конец маркер
k+ = 1;
delay(100);
if (!kbhit()) s=getch(); // перехватываем нажатую клавишу для
// останова программы
if (s==0x9) break;
} while (s!=0x13);
closegraph();
textmode(LASTMODE);
return;
}
// опрос порта по базовому адресу + смещение
int f_dba(int dba,int d[16])
{
int i,PA;
for (i=0; i<16; i++)
{
PA = inp(BA + dba + i);
d[i] = PA;
}
}
Запустим проект на компиляцию и выполнение командой <Ctrl>+<F9>. При этом, появится экран с запросом параметров считывания (см.рисунок 13). Задав номер FPGA = 3 и максимальное количество импульсов для подключенного датчика = 2048 (для нашего датчика RV-58N), нажмем <ENTER> и получим экран с визуализацией состояний двух энкодеров (см. рисунок 14-15). На рисунке 14 представлен экран состояния датчиков приращений при удаленном просмотре через сеть TCP/IP, но это уже тема отдельной статьи…
Рис. 13. Экран задания параметров считывания
Рис. 14. Удаленный просмотр датчиков приращений
Рис. 15. Экран визуализации состояний двух датчиков PDF
Заключение
Вот в принципе и все. Наслаждаемся аппаратным декодированием состояния шифратора приращений и визуальным просмотром…
Рассматриваемые в данной статье прошивка энкодера для ПЛИС в пакете XILINX 3.1, исходники и компиляция тестовой утилиты визуализации состояния датчика приращений и загрузчик ISP полностью приведены в виде ресурсов в теме «Журнал клуба программистов. Пятый выпуск» или непосредственно в архиве с журналом [4].
Ресурсы
- С. Бадло. Энкодер датчика PDF на ПЛИС. – ПРОграммист, Клуб ПРОграммистов, 2010, №4, с.40 http://raxp.radioliga.com/cnt/s.php?p=pro4.pdf
- Сайт производителя Xilinx http://www.xilinx.com
- С. Бадло. JTAG.Xilinx программатор. – Радиолюбитель, Минск, 2008, №7, с.38 http://raxp.radioliga.com/cnt/s.php?p=jtag.pdf
- Модули и проекты, использованные в статье http://raxp.radioliga.com/cnt/s.php?p=pro5.pdf
Статья из пятого выпуска журнала «ПРОграммист».
Скачать этот номер можно по ссылке.
Ознакомиться со всеми номерами журнала.
26th
Библиотека файловой системы AT45DB161
При разработке микроконтроллерных устройств с внешней памятью типа AT45DBxx приходится сталкиваться с ситуацией, когда блоки данных (файлы) записываются редко, обычно при обновлении ПО устройства, а иногда вообще только один раз — при изготовлении устройства. В таких случаях нет необходимости в использовании файловой системы дискового типа, таких как FAT…
Вячеслав Мовила
http://movilavn.narod.ru
родился 28 июля 1957 г.р., инженер. Интересы: фантастика, читаю много и с увлечением, имею неплохую библиотеку — собирал много лет. Шахматы, к сожалению сейчас не хватает времени. Но самое страстное увлечение и работа — это программирование, основной язык C++, а также, естественно Assembler, Pascal. WEB-программирование, создание динамических, интерактивных сайтов с использованием JavaScript, Java, PHP, MySQL. Базы данных, работа с CASE-средствами разработки информационным систем. Cхемотехника микропроцессорных и микроконтроллерных систем, программирование микроконтроллеров и DSP.
Проще и эффективнее использовать файловую систему последовательного доступа, наподобие ленточной. При этом файлы вместо названия имеют только индексы – последовательный номер файла на устройстве. Данные файла при записи записываются в конец свободного адресного пространства устройства, в этом случае при закрытии файла ему назначается уникальный номер (индекс). Доступ к файлу при чтении происходит по его индексу. На устройстве может находится не более 62 записанных файлов. Рассматриваемая библиотека организует такую файловую систему для микросхемы AT45DB161, порт CodeVision. Листинги библиотеки подробно прокомментированы [1…3]. Так, что при желании не потребуется больших усилий для портирования на другой компилятор и для внесения изменений с целью применения для другого типа микросхем серии AT45DBxx.
Краткое описание AT45DB161
AT45DB161 (см. рис.1) — является Flash памятью с последовательным интерфейсом, и идеально подходит для широкого спектра цифровых голосовых приложений, приложений визуализации, и приложений хранения программного кода и данных. 17 301 504 бит памяти данной ИС организованы в 4096 страниц по 528 байт каждая. Кроме памяти общего назначения ИС, также, имеет два SRAM буфера данных по 528 байт. Буферы обеспечивают возможность приема данных в режиме перепрограммирования страницы основной памяти, или считывание, или запись непрерывных потоков данных. Режим эмуляции EEPROM (с побитным или побайтным изменением) прост в применении, благодаря встроенной, трехступенчатой системе команд Read — Modify — Write. В отличие от стандартных типов Flash памяти, обращение к которым, происходит произвольным образом в режиме многочисленных адресных строк и при помощи параллельного интерфейса, память типа DataFlash использует последовательный интерфейс для обращения к своим данным в режиме последовательного доступа. ИС поддерживает SPI — режимы типа 0 и 3. Простой последовательный интерфейс облегчает разводку платы, увеличивает отказоустойчивость системы, минимизирует коммутационные шумы, а также, уменьшает размер корпуса и число необходимых активных выводов. ИС оптимизирована для использования в широком круге коммерческих и индустриальных приложений, для которых существенную роль играют высокая плотность размещения, малое число выводов, низкое напряжение питания, и низкое энергопотребление. ИС функционирует с тактовыми частотами, вплоть до 20 МГц при типовом потребляемом токе в режиме активного чтения 4 мА.
Рис. 1. Распиновка ИМС
Предпосылки выбора
Для обеспечения удобства внутрисистемного перепрограммирования, ИС AT45DB161 не требует высоких входных напряжений в режиме программирования. ИС питается от однополярного источника с напряжением от 2.5 В до 3.6 В, или от 2.7 В до 3.6 В, как в режиме программирования, так и в режиме чтения. Выборка ИС AT45DB161 производится по входу CS (активный низкий), а доступ к ИС обеспечивается посредством 3-х проводного последовательного интерфейса, состоящего из сигнала последовательного входа SI, последовательного выхода SO и последовательного тактового сигнала SCK (см. таблицу):
Таблица. Расшифровка сигналов ИМС
Все циклы программирования имеют встроенный контроль временных характеристик, а для проведения программирования предварительный цикл стирания не требуется. При поставке ИС от Atmel, старшая значащая страница массива памяти может не быть чистой. Другими словами, содержимое последней страницы может быть заполнено содержимым, отличным от FFH.
Отличительные особенности
- Однополярное напряжение питания от 2.5 В до 3.6 В, или от 2.7 В до 3.6 В
- Совместимость с последовательным периферийным интерфейсом типа SPI
- Максимальная тактовая частота 20 МГц
- Постраничный режим программирования:
- Одиночный цикл перепрограммирования (стирание плюс программирование)
- 4096 страниц основной памяти (528 байт на страницу)
- Поддержка страничного и блочного режимов стирания
- Два 528-ми байтных буфера данных SRAM, обеспечивающих прием данных в режиме перепрограммирования энергонезависимой памяти
- Поддержка режима непрерывного считывания полного массива данных
- Идеально для приложений теневого дублирования кода
- Низкое энергопотребление:
4 мА — типичный ток в режиме активного чтения
2 мкА — типичный потребляемый ток в режиме ожидания - Аппаратная функция защиты данных
- Входы сигналов SI, SCK, CS (активный низкий), RESET (активный низкий) и WP (активный низкий) устойчивы к логическим уровням 5 В
- Коммерческий и индустриальный диапазоны температур
Схема подключения
Рис. 2. Схема электрическая принципиальная тестового модуля считывания данных
Несмотря на то, что напряжение питания микросхемы AT45DB161 указано от 2.7 до 3.6 В, микросхема нормально работает при напряжении 5 В. В реальном проекте – используется питание 3 В. Поэтому подключил AT45DB161 на свою макетницу с микроконтроллером ATMega162 с 5-ти вольтовым питанием. Все тесты прошли исключительно хорошо: от самых простых до более сложных – воспроизведение звука с использованием ШИМ- модуляции. Микросхема AT45DB161 использовалась в корпусе SOIC, в котором отсутствует отдельный вывод BYSE, что видно на схеме принципиальной (см. рисунок 2) и было учтено при написании библиотеки.
Структура файловой системы
Первая страница (528 байт) отводится для хранения системной информации о файловой структуре устройства.
Первые 11 байт — это строка *AT45DB161#. Строка является идентификационной записью, которая показывает, что устройство отформатировано и готово к работе. В 12-ом байте содержится число, показывающее количество файлов на устройстве.
Далее идут 8-ми байтовые структуры, выполняющие функцию описателей файлов:
- в первых 2-х байтах адрес 1-ой страницы файла
- следующие 2 байта содержат количество страниц в файле
- последние 4 байта структуры — размер файла в байтах
Функции библиотеки
1. Инициализация файловой системы AT45DB161. unsigned char InitialFlash();
Возвращает значения:
0 — инициализировано.
1 — устройство отсутствует в системе.
2 — устройство не форматировано.
* Комментарий автора.
Внимание! При 6 В быстро выгорает, по крайней мере так было у меня.
2. Форматирование файловой системы AT45DB161 void FormatFlash();
3. Закрыть файл void CloseFile();
4. Открыть файл unsigned char OpenFile(char mode, unsigned char nfile);
Параметр mode:
r — Открыть файл в режиме чтения.
w — Открыть файл в режиме записи.
Параметр nfile:
В режиме чтения указывает индекс (номер) файла. В режиме записи не имеет значения, следует указать 0.
Возвращает значения:
0 — ошибка при открытии файла.
N(1…62) — индекс успешно открытого файла..
После успешного открытия файла будут доступны глобальные переменные статуса файла:
код:
/*
0 — Файл закрыт
1 — Файл открыт в режиме записи
2 — Файл открыт в режиме чтения
*/
unsigned char at45_file_mode = 0;
// Счетчик страниц при записи
unsigned int at45_cpwrite = 0;
// Номер текущей страницы
unsigned int at45_npage = 0;
// Индекс открытого файла
unsigned char at45_currfile = 0;
// Размер открытого файла/счетчик байт откр. файла при записи
unsigned long at45_cbfile = 0;
// Счетчик байт открытого файла при чтении
unsigned long at45_countb = 0;
// К-во страниц в файле
unsigned int at45_kpage = 0;
// Счетчик байт записанных/считанных в/из буфер
unsigned int at45_buffer_counter = 0;
5. Считать из файла count байт в буфер dest unsigned int ReadFile(char * dest, unsigned int count);
dest — Указатель на буфер чтения.
count — Количество байт для считывания.
Возвращает значения:
N — количество считанных байт.
6. Записать в файл count байт из буфера source. unsigned int WriteFile(char * source, unsigned int count);
source — Указатель на буфер записи.
count — Количество байт для записи.
7. Читать файл побайтно unsigned char ReadByte(unsigned char * flag);
flag — Указатель на переменную типа unsigned char. При чтении первого байта должен из вызывающей функции установлен в 0. При успешном чтении байта внутри функции ReadByte устанавливается в 1. При ошибке чтения или достижении конца файла внутри функции ReadByte устанавливается в 2.
Несмотря на то, что функция выглядит несколько неуклюже, она очень удобна при побайтном считывании по прерыванию.
Применение библиотеки
Для того, чтобы использовать библиотеку в проект ПО следует включить файлы: <at45db161.c> и <at45db161.h>. В файле <at45db161.h> следует скорректировать 2 настройки:
Первая:
код:
// Здесь следует определить пин порта мк. для CS AT45DB161!!!
#define DF_CHIP_SELECT PORTC.0
В строке 7 указать бит порта вывода который Вы будете использовать в качестве сигнала CS микросхемы AT45DB161:
Вторая:
Переопределить настройки интерфейса SPI в строке 38:
код:
// Включить SPI.
// SPI initialization
// SPI Type: Master
// SPI Clock Rate: 4000.000 kHz
// SPI Clock Phase: Cycle Start
// SPI Clock Polarity: High
// SPI Data Order: MSB First
#define SPI_ON SPCR=0x5c
Простой пример применения библиотеки:
код:
temp = InitialFlash();
if(temp) {
if(temp == 1) {
// И зацикливаемся — ус-во не обнаружено в системе
while(1);
}
// ИНАЧЕ УСТРОЙСТВО ОБНАРУЖЕНО НО НЕ ФОРМАТИРОВАННО!
// ФОРМАТИРУЕМ FLASH
FormatFlash();
}
// Записали первый файл:
OpenFile(‘w’, 0);
memset(Buff, 0, 128);
for(ic = 0;ic<256;ic++) Buff[ic] = ic;
WriteFile(Buff, 256);
WriteFile(Buff, 256);
WriteFile(Buff, 256);
WriteFile(Buff, 256);
WriteFile(Buff, 256);
CloseFile();
// Записали второй файл:
OpenFile('w', 0);
memset(Buff, 2, 128);
//for(ic = 0;ic<256;ic++) Buff[ic] = ic;
WriteFile(Buff, 128);
WriteFile(Buff, 55);
CloseFile();
// Записали третий файл:
OpenFile('w', 0);
memset(Buff, 3, 128);
//for(ic = 0;ic<256;ic++) Buff[ic] = ic;
WriteFile(Buff, 128);
WriteFile(Buff, 23);
CloseFile();
// Считали третий файл:
If (OpenFile(‘r’, 3)) {
do {
memset(Buff, 0, 528);
ic = ReadFile(Buff, 64);
} while(ic);
CloseFile();
}
//Считали второй файл:
if(OpenFile(‘r’, 2)) {
do {
memset(Buff, 0, 528);
ic = ReadFile(Buff, 64);
} while(ic);
CloseFile();
}
// Считали первый файл:
if(OpenFile(‘r’, 1)) {
memset(Buff, 0, 528);
do {
ic = ReadFile(Buff, 64);
} while(ic);
CloseFile();
}
Заключение
Как видите, использование библиотеки последовательного доступа значительно упрощает разработку целого ряда устройств с голосовым сопровождением на основе Flash- памяти.
** Важное замечание.
При считывании последнего блока данных из файла в буфер заносится действительное оставшееся количество байтов, а их количество указывается в возвращаемом функцией значении.
*** Комментарий автора.
Программа, которая умеет открывать файлы мультимедиа типа WAV, идентифицировать их, на соответствии требованиям закачки (частота дискретизации, количество каналов и т.д.) и пересылать их в микроконтроллерную систему.
Рассматриваемые в данной статье исходники библиотеки файловой системы последовательного доступа полностью приведены в виде ресурсов в теме «Журнал клуба программистов. Пятый выпуск» или непосредственно в архиве с журналом.
Ресурсы
- Ссылка на скачивание библиотеки LIB_AT45
http://movilavn.narod.ru/LIB_AT45.rar - Проект для микроконтроллера ATMega162 — загрузка файлов в AT45DB161
http://movilavn.narod.ru/AT45LOAD.rar - Проект для ПК (Builder C++ 6). LoadAT45DB***
http://movilavn.narod.ru/LoadAT45DB.rar - Модули и проекты, использованные в статье
http://programmersclub.ru/pro/pro5.pdf
Статья из пятого выпуска журнала «ПРОграммист».
Скачать этот номер можно по ссылке.
Ознакомиться со всеми номерами журнала.
Облако меток
css реестр ассемблер timer SaveToFile ShellExecute программы массив советы word MySQL SQL ListView pos random компоненты дата LoadFromFile form база данных сеть html php RichEdit indy строки Win Api tstringlist Image мысли макросы Edit ListBox office C/C++ memo графика StringGrid canvas поиск файл Pascal форма Файлы интернет Microsoft Office Excel excel winapi журнал ПРОграммист DelphiКупить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)