Статьи по Assembler
Browse info для ассемблера
Всякий сколько-нибудь значительный программный проект содержит сотни, а то и тысячи идентификаторов. Как у программистов хватает фантазии их придумывать - это отдельная тема для исследования, пока что ждущая своего Фрейда, или Павлова, или Кастанеду, или, на худой конец, Маркеса.
Тут сразу следует напомнить о настоящих ассемблерщиках, которые не признают даже структурных директив MASM 6.1+. Ко всем прочим бедам они подбрасывают своему серому веществу еще и задачку придумывать имена для прорвы меток. Не заботясь о своем здоровье, надо сказать. Потому что, как известно, ничто так не разрушает здоровье, как работа, связанная с необходимостью принятия микрорешений. Позволим себе напомнить вам общеизвестный факт, что уровень преступности, суицида, алкоголизма, сексуальных извращений и политической активности среди сортировщиков куриных яиц примерно в восемь раз выше, чем среди трактористов!
А ведь сортировщики куриных яиц еще не сталкиваются с такой проблемой, как глобальность идентификаторов в пределах модуля! Спасибо любимой Microsoft за то, что хоть метки стали локальны в пределах процедуры. Впрочем, специально для садомазохистов разработчики предусмотрели директиву OPTION NOSCOPED - кайфуйте, ребята.
Каждый крутится как может. Шаблонно мыслящие конформисты идут на поводу у Чарльза Симони и украшают свои исходники венгерской тавтологией вроде lpSomeTable. Аристократические отпрыски Йеля и Итона, сложив губки гузкой, выстукивают холенымы ногтями имя функции: PleaseGiveMeAnErrorCode. Раздолбанные хакеры, вытерев рукавом пиво с клавиатуры, без раздумий используют получившуюся комбинацию: p9ijhbgfd, и в течение следующей минуты навешивают на нее как минимум сорок строк кода. Стиль русских программистов старшего поколения, заставших время, когда их называли советскими программистами, особый, и не меняется уже много лет: vodka362, kolbasa220. Изредка попадающиеся в этой экологически нездоровой среде женщины широко используют свои специфические ассоциации: carnation, champagne, alwaysultraplus. Педанты же как всегда педантичны: proc_for_scan_table_of_strings_00000001.
А вот откуда берется файл .bsc - разобраться надо, потому что без понимания этого процесса запустить Browse Info в ассемблерном проекте вряд ли удастся.
Процесс создания базы данных .bsc, как и следует ожидать, происходит на обоих этапах построения проекта - и на этапе компиляции, и на этапе компоновки.
Компилятор (будь то cl.exe для C++ или ml.exe для MASM) создает промежуточный файл базы данных, содержащий, естественно, только информацию об идентификаторах данной единицы компиляции - cpp-файла или asm-файла. Этот промежуточный файл имеет расширение .sbr и имя, по умолчанию совпадающее с именем компилируемого файла. Размещается он там же, где и создаваемый объектный файл - в папке Debug. Для того, чтобы компилятор озаботился созданием sbr-файла, в его командную строку следует включить опцию /FR или /Fr.
Опции отличаются друг от друга тем, что первая из них включает в sbr-файл информацию обо всех идентификаторах (в том числе локальных), а вторая - только об объявленных внешними и публичными. Выбор - за программистом.
Опции предоставляют возможность изменить местоположение и имя создаваемого sbr-файла, как, например: /FR"..\Support Files\browseinfo.sbr"
Поскольку компиляция файлов C/C++ поддерживается MS Developer Studio, то для того, чтобы заставить компилятор создавать sbr-файл, следует запустить диалог Project Settings, открыть вкладку C/C++, выбрать в списке Category пункт Listing Files, и поставить галочку в чекбоксе Generate browse info. Если нужно заменить местоположение или имя sbr-файла, следует воспользоваться полем Intermediate browse info file destination. А замена опции /FR на опцию /Fr производится галочкой в чекбоксе Exclude local variables from browse info.
Для ассемблерных файлов опцию в нужной форме следует прямо указать в командной строке на вкладке Custom Build при настройке MS Developer Studio.
На этапе компоновки проекта из промежуточных sbr-файлов, созданных компилятором для каждого из модулей, собирается один на весь проект bsc-файл. Правда, слова "на этапе компоновки" отнюдь не означают, что этим занимается сам компоновщик. Для сборки bsc-файла существует специальная утилита - bscmake.exe, находящаяся в папке исполняемых файлов Vc\bin MS Developer Studio.
Включить вызов утилиты bscmake.exe можно с помощью чекбокса Build browse info file, находящегося на вкладке Browse Info диалога Project Settings. Там же можно при необходимости поменять местоположение и имя bsc-файла, подавить выдачу баннерного текста и задать другие опции командной строки утилиты.
Учитывая обширность заголовочных файлов API, следует ожидать, что удовольствие от применения Browse Info хорошо оплачено ресурсами компьютера. Так оно и есть. Sbr-файл, образованный из чистого windows.h, весит почти восемьсот кило. А образованный из него bsc-файл - в два с лишним раза больше. При включенном Browse Info соответственно увеличивается время компиляции и компоновки проекта. Это обстоятельство могло бы превратить жизнь программиста в сущий ад, если бы не добрые дяди из Microsoft, предусмотревшие инкрементную обработку этих файлов. Суть ее в том, что сборка bsc-файла выполняется каждый раз не заново, а только с учетом реально произошедших изменений в проекте. Определить же, какие именно изменения произошли, утилита bscmake.exe умудряется очень просто. Всякий раз после очередной сборки она усекает до 0 размер участвовавших в сборке sbr-файлов. Таким образов в следующем сеансе сборки примут участие только те sbr-файлы, которые с прошлого раза прошли реальную перекомпиляцию.
Ну и, наконец, самое интересное. Как настроить Browse Info для работы с ассемблерным проектом. Специфика организации рабочей среды такова, что имеет смысл рассматривать два варианта настройки:
Вариант с одним проектом:
/FR"Debug\$(InputName).sbr"Это необходимо сделать для всех asm-файлов, для которых вы хотели бы иметь включенным средство Browser Info.
| Как видно, sbr-файлы будут помещаться в папку Debug с именем, соответствующим имени asm-файла. Эта папка выбрана нами потому, что по умолчанию в нее же помещаются sbr-файлы модулей, написанных на C/C++. Учтите, что в начале работы с проектом она не существует, поэтому попытка компиляции даст ошибку. Создайте ее вручную. |
#include
Debug\*.sbr Таким образом вы дадите утилите bscmake.exe команду включить в сборку все sbr-файлы, содержащиеся в папке Debug
Следует учесть одно неприятное обстоятельство. Рабочая среда определяет необходимость запуска утилиты bscmake.exe по факту выполнения компиляции хотя бы одного файла C/C++. Ассемблерные же файлы, будучи компилируемы посредством Custom Build, к сожалению, такой команды рабочей среде не дают. Поэтому все изменения в составе идентификаторов в ассемблерных файлах остаются втуне до тех пор, пока не будет перекомпилирован хотя бы один cpp-файл проекта и после этого не выполнена его компоновка. В чисто ассемблерных проектах придется вручную вызывать компиляцию файла brinfo.cpp.
В этой беде мог бы помочь вынос вызова утилиты bscmake.exe на этап Post-build step. Однако, этот фокус не проходит: дело в том, что однажды активизировавшись, Browse Info открывает bsc-файл и держит его в дальнейшем в открытом состоянии, запрещая таким образом запись в него всем внешним программам. Этот запрет отменяется только на этапе компоновки проекта, но не на этапе Post-build step.
Вариант с несколькими проектами базируется на тех же идеях, что и вариант с одним проектом, но с учетом некоторых коррекций:
Инструкция программиста mycall
Приложение MyCall - это пользовательский интерфейс Remote Access Service для Windows 95/98. Приложение разработано как учебно-экспериментальная задача, целью которой является демонстрация отличий в реализации приложений на ассемблере и на C++. См. статью Зачем он нужен, этот ассемблер?.
Исходный текст реализации на C++ состоит из файлов:
Полный комплект файлов, необходимых для компиляции приложения, содержится в zip-файле mycallcb.zip (13192 байта)
Исходный текст реализации на ассемблере состоит из файлов:
Полный комплект файлов, необходимых для компиляции приложения, содержится в zip-файле mycallab.zip (15913 байта)
компиляция приложения
Компиляция реализации на C++:
Компиляция реализации на ассемблере:
Это, в свою очередь, диктует особенности программных решений. Был сознательно выбран хаотический стиль программирования, не использующий практически никаких характерных для C++ объектно-ориентированных средств организации программ. (В результате проект, например, изобилует глобальными переменными, что в общем случае следовало бы считать очень плохим стилем.) Также практически не использовались и специфические приемы ассемблерного программирования.
Но зато, положив рядом два листинга - на C++ и на ассемблере - желающие имеют теперь возможность как угодно глубоко разобраться в отличиях реализаций. Может быть полезно также сравнить дизассемблированный код приложения на C++ с реализацией на ассемблере, что позволяет сделать встроенный отладчик MS Developer Studio.
Кстати, о стиле. Для таких маленьких приложений, вполне возможно, хаотический стиль программирования выглядит более предпочтительным, чем объектно-ориентированный, как с точки зрения читабельности программы, так и с точки зрения минимизации накладных расходов: времени на разработку, объема исходного текста и результирующего объема кода.
Работа приложения начинается с передачи управления функции WinMain. По своему построению это обычная для win32 функция, выполняющая следующие задачи:
Некоторым отличием от общепринятых норм построения WinMain является то, что в качестве главного окна приложения используется не обычное окно, а окно диалога, описание которого содержится в файле описания ресурсов. Впрочем, это частый прием для небольших утилитоподобных приложений вроде MyCall.
Кроме того, в функции WinMain выполняется проверка запуска второго экземпляра приложения. Для этого применяется специальный способ, основанный на обмене регистрируемым глобальным сообщением.
Все внешние данные приложения хранятся в двух файлах: mycall.txt и mycall.ini. Они оба загружаются соответствующими функциями, вызываемыми из WinMain в начале работы приложения.
Файл mycall.ini содержит данные о позиции окна приложения на рабочем столе и о состоянии выбора списков соединений, телефонов и логинов на момент завершения предыдущего сеанса работы приложения. Это удобная возможность, позволяющая пользователю сохранять привычную ему рабочую среду.
Файл mycall.ini загружается функцией load_ini однократно в начале работы приложения. На основании содержащихся в нем данных устанавливаются значения соответствующих переменных. Если файл mycall.ini не существует, как это бывает, например, при первом запуске приложения, то в переменных остаются значения по умолчанию.
Файл mycall.ini формируется и записывается функцией load_ini непосредственно перед завершением работы приложения. Функция вызывается оконной процедурой superprocedure при обработке ею сообщения WM_DESTROY.
Файл mycall.txt содержит данные, задаваемые пользователем для соединения с провайдером: имена соединений, номера телефонов, логины и пароли.
Он загружается функцией load_data однократно в начале работы приложения. В последующем содержимое файла хранится в специальном буфере, который сканируется всякий раз, когда требуется изменить содержимое соответствующих списков. Это делает функция change_con.
Приложение MyCall не изменяет содержимого файла mycall.txt и не сохраняет его. Отредактировать файл может только пользователь с помощью какого-нибудь текстового редактора. Формат файла должен строго соблюдаться пользователем, так как приложение MyCall не проверяет его при загрузке файла. Ошибки формата не приводят к фатальным последствиям, но данные для работы с провайдерам становятся некорректными.
Общую диспетчеризацию в MyCall, как того и требует архитектура приложений win32, выполняет оконная процедура superprocedure. Она обрабатывает сообщения:
После выбора соединения, телефона и логина, пользователь, нажав кнопку, переводит приложение в режим дозвона. При этом:
Функция ras_dial на основании данных, выбранных пользователем, заполняет структуру RASDIALPARAMS и вызывает функцию API RasDial.
Для мониторинга процесса дозвона используется callback-функция ras_dial_func типа RasDialFunc (см. Platform SDK). Эта функция отображает соответствующие сообщения в строке статуса и, в случае неудачи установления соединения, посылает оконной процедуре сообщение WM_USER с параметром "0" для выполнения повторных попыток дозвона.
Использовать механизм сообщений в данном случае необходимо, так как callback-функция вызывается сервисом RAS в отдельной нити, и, следовательно, не синхронизирована с главной нитью приложения.
Если соединение установлено успешно, callback-функция ras_dial_func запускает нить монитора разрыва ras_monitor_func. Такой прием пришлось применить потому, что в сервисе RAS не предусмотрено средств для контроля работающего соединения, и, следовательно, приложение не может никаким иным способом узнать о факте разрыва соединения, например, при потере связи. Нить монитора разрыва каждые 200 мс опрашивает состояние соединения, и при обнаружении его разрыва сообщает об этом основной нити приложения путем посылки сообщения WM_USER с параметром "1", а затем завершается. Получив это сообщение, приложение переходит в режим выбора соединения.
Для прекращения дозвона и принудительного разрыва установленного соединения используется функция ras_hangup. Она вызывает функцию API RasHangUp.
Важным элементом является последующий цикл, опрашивающий состояние соединения. Дело в том, что сервис RAS может иметь задержки реакции на вызов функций API до нескольких секунд. Если в течение времени этой задержки попытаться вызвать, например, функцию RasDial, последствия, как правило, бывают фатальны. Примененный здесь цикл позволяет гарантированно дождаться окончания задержки.
Несмотря на то, что приложение MyCall разрабатывалось как учебно-экспериментальное, оно вполне функционально и может удовлетворить потребности многих пользователей.
Компиляция файлов .asm
Этот материал дополняет статью MS Developer Studio - среда разработки для ASM. Здесь приведен формат командной строки компилятора MASM 6.1+, расшифровка ее опций и комментарии по их применению при работе в среде MS Developer Studio.
Командная строка MASM имеет вид:
ML [/options] filename [/options] filename [/link linkoptions]
Здесь:
| опция | назначение | применение для win32 |
| /AT | Enable tiny model (.COM file) Создать файл в формате .com (модель tiny) |
Не применяется, так как формат исполняемого файла .com не используется в Win32. |
| /Bl |
Use alternate linker Использовать альтернативный компоновщик |
Обычно не применяется, так как возможностей link.exe вполне достаточно. Используется опция /c |
| /c | Assemble without linking Только компиляция, без компоновки |
Обязательно для применения в среде MS Developer Studio, чтобы выполнять компоновку отдельным этапом. |
| /Cp | Preserve case of user identifiers Сохранение регистра пользовательских идентификаторов |
Применение не обязательно, но возможно для дополнительного контроля синтаксиса. Вызывает ошибку "A2006: undefined symbol" при несовпадении регистра в объявлении идентификатора и обращении к нему. Позволяет избежать ошибок на этапе компоновки в случае, если идентификатор объявлен с неверным регистром. |
| /Cu | Map all identifiers to upper case Приведение всех пользовательских идентификаторов к верхнему регистру |
Не применяется, так как компоновка приложений Win32 чувствительна к регистру. |
| /Cx | Preserve case in publics, externs Сохранение регистра идентификаторов, объявленных публичными и внешними |
В применении нет необходимости. Регистр идентификаторов имеет смысл на этапе компоновки, но не на этапе компиляции. |
| /coff | Generate COFF format object file Создать файл в формате COFF |
Применение обязательно: это стандартный для windows формат объектных и исполняемых файлов. |
| /D |
Define text macro Описание текстового макроса |
Применяется по усмотрению программиста. Аналог директив EQU или =. Если текст содержит пробелы, его следует взять в кавычки. Обычно используется в отладочном версии приложения для объявления имени DEBUG. |
| /EP | Output preprocessed listing to stdout Вывод листинга препроцессора в stdout |
Обычно применять нет необходимости. Листинг препроцессора представляет собой исходный текст вместе с включаемыми файлами. |
| /F |
Set stack size (bytes) Определить размер стека (байт) |
Практически не применяется. То же, что опция /STACK компоновщика link.exe. Обычно используется значение по умолчанию - 1 Мбайт. |
| /Fe |
Name executable Имя исполняемого файла |
Не применяется, так как с учетом опции /c компилятор не создает исполняемого файла |
| /Fl[file] | Generate listing Создание файла листинга |
Обычно не применяется, так как средства MS Developer Studio, как правило, достаточны для работы с текстом приложения. |
| /Fm[file] | Generate map Создание map-файла |
Не применяется, так как map-файл создается компоновщиком, а с учетом опции /c компилятор не вызывает компоновщик |
| /Fo |
Name object file Имя объектного файла |
Обычно не применяется. Позволяет задать obj-файлу имя, отличное от имени asm-файла. |
| /FPi | Generate 80x87 emulator encoding Включение кода эмулятора сопроцессора 80x87 |
Начиная с выхода в свет процессора 486 не применяется, так как с тех пор арифметический сопроцессор является неотъемлемой частью современных процессоров. |
| /Fr[file] | Generate limited browser info Включить ограниченную информацию броузера |
Применение менее предпочтительно, чем /FR, так как в информацию броузера не включа.тся сведения о локальных идентификаторах. |
| /FR[file] | Generate full browser info Включить полную информацию броузера |
Позволяет получать быстрый доступ к любому идентификатору во всем пространстве проекта и заголовочных файлов API win32. См. статью об этом. |
| /G |
Use Pascal, C, or Stdcall calls Использовать соглашения вызова Pascal, C или Stdcall |
В применении нет необходимости. Обычно использование соглашений вызова stdcall регламентируется директивой .model в тексте программы. |
| /H |
Set max external name length Установить максимальную длину внешних имен |
Обычно не применяется. Значение по умолчанию - 31, и его достаточно для работы в среде win32. |
| /I |
Add include path Добавить путь для inc-файлов |
Не применяется, так как собственных возможностей MS Developer Studio обычно достаточно для определения путей к inc-файлам. Допускается использовать до 10 опций /I. |
| /link | Опции командной строки компоновщика и подключаемые библиотеки |
Не применяется, так как компоновка отключена опцией /c. |
| /nologo | Suppress copyright message Не показывать баннерный текст компилятора |
Как правило, следует применяеть, так как баннерный текст смысловой нагрузки при разработке проекта не несет. |
| /Sa | Maximize source listing Листинг максимального формата |
Применяется редко, так как собственных средств MS Developer Studio обычно достаточно для работы с исходным и компилированным текстом программы, и в выдаче листинга нет необходимости. |
| /Sc | Generate timings in listing Включить в листинг синхронизацию |
То же |
| /Sf | Generate first pass listing Листинг первого прохода |
То же |
| /Sl |
Set line width Длина строки листинга, символов: 60...255 или 0. |
То же |
| /Sn | Suppress symbol-table listing Не включать в листинг таблицу символов |
То же |
| /Sp |
Set page length Высота страницы листинга, строк: 10...255 или 0. |
То же |
| /Ss |
Set subtitle Текст подзаголовков листинга |
То же |
| /St |
Set title Текст заголовка листига |
То же |
| /Sx | List false conditionals Включить в листинг все фрагменты условной компиляции |
То же |
| /Ta |
Assemble non-.ASM file Компилировать не-.asm файлы |
Обычно не применяется. Служит для компиляции файлов, имя котрых имеет расширение, отличное от .asm. |
| /w | Same as /W0 /WX То же, что /W0 /WX |
См. далее. |
| /WX | Treat warnings as errors Трактовать предупреждения как ошибки |
Обычно в применении нет необходимости. В случае возникновения предупреждений компиляция завершается неуспешно. |
| /W |
Set warning level Установить уровень предупреждеинй |
Обычно в применении нет необходимости. Устанавливает перечень событий компиляции, трактуемых как предупреждения. |
| /X | Ignore INCLUDE environment path Игнорировать путь, установленный переменной окружения INCLUDE |
Обычно не применяется, так как при работе в среде MS Developer Studio переменная окружения INCLUDE не используется. |
| /Zd | Add line number debug info Включить отладочную информацию в виде номеров строк |
Обычно не применяется, так как на этапе отладки более целесообразно использовать опцию /Zi. |
| /Zf | Make all symbols public Объявить все имена публичными |
Обычно не применяется. |
| /Zi | Add symbolic debug info Включить полную отладочную информацию |
Обязательно применяется на этапе отладки. Формат отладочной информации MASM полностью совместим с используемым встроенным отладчиком MS Developer Studio. |
| /Zm | Enable MASM 5.10 compatibility Включить совместимость с MASM 5.10 |
Обычно не применяется. Отключает полезные для прикладного программирования свойства MASM, введенные в версиях 6.1+. |
| /Zp[n] | Set structure alignment Установить выравнивание структур |
Может быть использован для установки принятого в win32 выравнивания по умолчанию - на 8 байт. Однако обычно в применении нет необходимости, так как в win32 используется два варианта выравнивания структур - на 4 и на 8 байт, и определять их выравнивание целесообразно непосредственно в описании структуры директивой STRUCT. Возможные значения для этой опции - 1, 2, 4 и 8 (последнее - в версиях MASM 6.13 и выше). |
| /Zs | Perform syntax check only Выполнять только проверку синтаксиса |
Обычно не применяется. Подавляет формирование объектного модуля. |
Типовая командная строка для этапа отладки (активен проект Win32 Debug) имеет вид:
ml.exe /c /coff /FR"..\browseinfo\$(InputName).sbr" /nologo /Zi /DDEBUG $(InputPath)
Отключена компоновка, формируется объектный модуль в формате COFF, формируется sbr-файл, содержищий информацию для Browse Info, подавлена выдача баннерной информации, в объектный модуль включена полная отладочная информация, определена константа DEBUG.
Типовая командная строка для чистового этапа (активен проект Win32 Release) имеет вид:
ml.exe /c /coff /nologo $(InputPath)
Отключена компоновка, формируется объектный модуль в формате COFF, подавлена выдача баннерной информации.
Main.asm для mycall (ассемблер)
Это основной файл приложения MyCall на ассемблере. Этот файл в текстовом формате вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файле mycallab.zip (15913 байт). Имеется также Инструкция программиста.
Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
;Включаемые файлы:
;@struct.inc - файл структурных макросов
;windows.inc - файл заголовков win32
;main.inc - файл заголовков приложения MyCall
include @struct.inc
include windows.inc
include main.inc
;ГЛАВНАЯ ФУНКЦИЯ ПРИЛОЖЕНИЯ
;///////////////////////////////////////////////////////////// WinMain
.data?
mw_class WNDCLASSEX{}
loop_message MSG{}
win_dim RECT{}
.const
mw_class_name db "MainWindowClass",0
my_message_name db "MyCallMessage",0
.code
WinMain PROC PUBLIC hinst,prev_hinst,command_line,cmd_show
mov dat_buffer,0
mov online,FALSE
;Получение дескриптора экземпляра приложения
;Необходимое действие, так как предполагается компиляция приложения без
;подключения runtime-библиотеки. Подробнее...
invoke GetModuleHandleA,NULL
mov hinst,eax
;Создание главного окна
;Обычное действие, с которого начинаются большинство приложений.
;Единственное отличие в том, что в качестве главного окна в MyCall
;используется окно диалога, описанного в файле ресурсов
;Регистрируется класс главного окна:
mov mw_class.cbSize,sizeof(WNDCLASSEX)
mov mw_class.style,NULL
mov mw_class.lpfnWndProc,offset superprocedure
mov mw_class.cbClsExtra,0
mov mw_class.cbWndExtra,DLGWINDOWEXTRA
mov eax,hinst
mov mw_class.hInstance,eax
invoke LoadIconA,eax,103
mov mw_class.hIcon,eax
mov mw_class.hIconSm,NULL
invoke LoadCursorA,NULL,IDC_ARROW
mov mw_class.hCursor,eax
mov mw_class.hbrBackground,COLOR_WINDOW
mov mw_class.lpszMenuName,NULL
mov mw_class.lpszClassName,offset mw_class_name
@if(ecx==3)
mov ecx,0
@endif
@endif
@if(ecx==0)
@if(ebx==0)
@if(byte ptr[esi]!=0)
@if(byte ptr[esi]!=1)
@push ecx,edx,ebx,esi
invoke SendMessageA,conn_window,CB_ADDSTRING,0,esi
@pop ecx,edx,ebx,esi
inc edx
@if(edx>=MAX_CON)
@break
@endif
@endif
@endif
@endif
@endif
inc ebx
inc esi
@endw
;По данным файла mycall. ini устанавливается прошлая позиция списка conn_window
@if(current_con>=edx)
mov current_con,0
@endif
invoke SendMessageA,conn_window,CB_SETCURSEL,current_con,0
;По состоянию списка conn_window формируются остальные списки
call change_con
;ЦИКЛ ОЖИДАНИЯ СООБЩЕНИЙ И ЗАВЕРШЕНИЕ ПРИЛОЖЕНИЯ
;Неотъемлемый элемент приложений для Windows. В MyCall никаких особенностей не имеет
;Поскольку MyCall собирается без runtime-библиотеки, для завершения работы
;обязательно использовать функцию ExitProcess. Подробнее...
msg_loop:
invoke GetMessageA,offset loop_message,NULL,0,0
@if(!eax)
invoke ExitProcess,loop_message.wParam
@endif
invoke TranslateMessage,offset loop_message
invoke DispatchMessageA,offset loop_message
jmp msg_loop
bad:
invoke fatal,eax
invoke ExitProcess,EXIT_COMMON_ERROR
WinMain ENDP
;ОКОННАЯ ПРОЦЕДУРА
;Стандартный элемент приложений для Windows. В MyCall особенностей не имеет.
;///////////////////////////////////////////////////////////// Оконная процедура
.data?
win_pos RECT{}
.code
superprocedure PROC window_from,message,w_param,l_param
mov eax,message
;Обработка сообщения WM_COMMAND, передаваемого элементами управления диалога
@if(eax==WM_COMMAND)
mov eax,w_param
mov cl,16
shr eax,cl
;Обработка уведомления CBN_SELCHANGE, передаваемого списками при изменении позиции:
;1000: список соединений conn_window. Изменяет содержание phon_window и user_window
;1001: список телефонов phon_window. Устанавливает новый телефон для текущего соединения con_phone[current_con]
;1002: список логинов user_window. Устанавливает новый логин для текущего соединения con_user[current_con]
@if(eax==CBN_SELCHANGE)
mov eax,w_param
and eax,0ffffh
@if(ax==1000)
mov eax,conn_window
and current_con,0ffh
mov esi,offset current_con
@elseif(ax==1001)
mov eax,phon_window
mov esi,offset con_phone
add esi,current_con
@elseif(ax==1002)
mov eax,user_window
mov esi,offset con_user
add esi,current_con
@else
jmp sp_nok
@endif
push esi
invoke SendMessageA,eax,CB_GETCURSEL,0,0
pop esi
@if(eax==CB_ERR)
xor eax,eax
@endif
mov [esi],al
call change_con
jmp sp_ok
@endif
; Обработка уведомления BN_CLICKED, передаваемого кнопкой при клике:
;в состоянии online=TRUE прекращает дозвон (разрывает соединение) и включает списки
;в состоянии online=FALSE отключает списки и начинает дозвон
@if(eax==BN_CLICKED)
mov eax,w_param
and eax,0ffffh
@if(eax==1003)
@if(online)
invoke EnableWindow,butt_window,FALSE
call ras_hangup
invoke EnableWindow,butt_window,TRUE
push FALSE
call disable_controls
@else
push TRUE
call disable_controls
call ras_dial
@endif
jmp sp_ok
@endif
@endif
jmp sp_nok
;Обработка сообщения WM_USER, используемого в MyCall нитью монитора разрыва.
;Обнаружив факт разрыва соединения, нить монитора передает WM_USER. Если был коннект,
;то включаются списки, в противном случает выполняется повторный дозвон
@elseif(eax==WM_USER)
call ras_hangup
@if(l_param)
push FALSE
call disable_controls
@else
call ras_dial
@endif
jmp sp_ok
;Обработка сообщения WM_MOVE: запоминание новой позиции окна
;для последующей записи ее в файл mycall.ini
@elseif(eax==WM_MOVE)
invoke GetWindowRect,main_window,offset win_pos
@if(eax)
push win_pos.left
pop main_win_left
push win_pos.top
pop main_win_top
@else
mov eax,l_param
and eax,0ffffh
mov main_win_left,eax
mov eax,l_param
mov cl,16
rcr eax,cl
and eax,0ffffh
mov main_win_top,eax
@endif
jmp sp_ok
;При завершении работы приложения записать файл mycall.ini,
;и освободить память
@elseif(eax==WM_DESTROY)
call save_ini
@if(dat_buffer)
invoke GlobalFree,dat_buffer
@endif
invoke PostQuitMessage,EXIT_NORMAL
jmp sp_ok
; При закрытии главного окна заблокировать кнопку
;и разорвать соединение
@elseif(eax==WM_CLOSE)
invoke EnableWindow,butt_window,FALSE
@if(online)
call ras_hangup
@endif
jmp sp_nok
;Обработка глобального оконного сообщения, используемого для
;взаимодействия экземпляров приложения. Получив это сообщение,
;экземпляр, запущенный первым, сообщает дескриптор своего главного окна.
;Сообщение, полученное вторым, принимает от первого дескриптор его главного окна,
;выводит его на первый план и завершается. Подробнее...
@if(eax==my_message)
mov ebx,w_param
@if(ebx!=main_window)
@if(!l_param)
invoke SendMessageA,HWND_BROADCAST,eax,main_window,1
@else
invoke SetForegroundWindow,w_param
invoke ExitProcess,EXIT_OVERLOADED
@endif
@endif
jmp sp_ok
@endif
;Возврат необработанных сообщений системе
sp_nok:
invoke DefWindowProcA,window_from,message,w_param,l_param
ret
;Завершение оконной процедуры для обработанных сообщений
sp_ok:
xor eax,eax
ret
superprocedure ENDP
;ЗАГРУЗКА ФАЙЛА ИНИЦИАЛИЗАЦИИ
;Файл mycall.ini хранит состояние списков и положение главного окна на момент
;завершения предыдущего сеанса работы приложения
;///////////////////////////////////////////////////////////// Загрузка файла инициализации
.data?
ini_filedd ?
buffer db INI_FILE_LENGTH dup(?)
bytes_readwrite dd ?
.const
ini_file_name db "mycall.ini",0
.code
load_ini PROC
;Инициализация глобальных переменных
mov main_win_left,MAIN_WIN_DEFAULT_LEFT
mov main_win_top,MAIN_WIN_DEFAULT_TOP
mov current_con,0
mov ebx,MAX_CON
@while(ebx)
dec ebx
mov [con_phone+ebx],0
mov [con_user+ebx],0
@endw
;Попытка загрузки файла mycall.ini
;В случае неудачи используются значения по умолчанию
invoke CreateFileA,offset ini_file_name,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
@if(eax!=INVALID_HANDLE_VALUE)
;Чтение файла mycall.ini
push eax
invoke ReadFile,eax,offset buffer,INI_FILE_LENGTH,offset bytes_readwrite,NULL
@if(eax)
;Приведение позиции окна к фактическим размерам экрана и запоминание
xor edx,edx
mov dx,[word ptr buffer]
push edx
invoke GetSystemMetrics,SM_CXSCREEN
sub eax,10
pop edx
@if(edx<=eax)
mov main_win_left,edx
@endif
xor edx,edx
mov dx,[word ptr buffer+2]
push edx
invoke GetSystemMetrics,SM_CYSCREEN
sub eax,10
pop edx
@if(edx<=eax)
mov main_win_top,edx
@endif
;Запоминание позиций списков телефонов и логинов
xor edx,edx
mov dl,[byte ptr buffer+4]
mov current_con,edx
xor ecx,ecx
xor ebx,ebx
@while(ecx
mov ax,[word ptr buffer+5+ebx]
mov [con_phone+ecx],al
mov [con_user+ecx],ah
add ebx,2
inc ecx
@endw
@endif
pop eax
invoke CloseHandle,eax
@endif
ret
load_ini ENDP
;СОХРАНЕНИЕ ФАЙЛА ИНИЦИАЛИЗАЦИИ mycall.ini
; В файле сохраняются положение главного окна на экране и позиции
;списков на момент завершения работы приложения
;============================================================= Сохранение файла инициализации
save_ini PROC
invoke CreateFileA,offset ini_file_name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
@if(eax!=INVALID_HANDLE_VALUE)
push eax
mov eax,main_win_left
mov [word ptr buffer],ax
mov eax,main_win_top
mov [word ptr buffer+2],ax
mov eax,current_con
mov [buffer+4],al
xor ecx,ecx
xor ebx,ebx
@while(ecx
mov al,[con_phone+ecx]
mov ah,[con_user+ecx]
mov [word ptr buffer+5+ebx],ax
add ebx,2
inc ecx
@endw
pop eax
push eax
invoke WriteFile,eax,offset buffer,INI_FILE_LENGTH,offset bytes_readwrite,NULL
pop eax
invoke CloseHandle,eax
@endif
ret
save_ini ENDP
;ЗАГРУЗКА ФАЙЛА ДАННЫХ mycall.dat
;Файл содержит имена соединений, телефоны и логины
;///////////////////////////////////////////////////////////// Загрузка файла данных
.data?
dat_file dd ?
dat_file_size dd ?
.const
dat_file_name db "mycall.txt",0
.code
load_data PROC
;Открытие файла, подготовка буфера для него и считывание файла в буфер
invoke CreateFileA,offset dat_file_name,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
@if(eax==INVALID_HANDLE_VALUE)
mov eax,FATAL_DAT_FILE_OPEN
jmp ld_out
@endif
mov dat_file,eax
invoke GetFileSize,eax,NULL
@if(eax==0ffffffffh)
mov eax,FATAL_DAT_FILE_SIZE
jmp ld_out
@endif
mov dat_file_size,eax
add eax,3
invoke GlobalAlloc,GMEM_FIXED,eax
@if(!eax)
mov eax,FATAL_DAT_BUF_ALLOC
jmp ld_out
@endif
mov dat_buffer,eax
invoke ReadFile,dat_file,eax,dat_file_size,offset bytes_readwrite,NULL
@if(!eax)
mov eax,FATAL_DAT_FILE_READ
jmp ld_out
@endif
;Замена слэшей, CR, LF и пробелов на 0h, добавление в конец 1h
xor ebx,ebx
mov esi,dat_buffer
@while(ebx
mov al,byte ptr [esi][ebx]
xor edx,edx
@if(al==0dh)
inc edx
@elseif(al==0ah)
inc edx
@elseif(al==' ')
inc edx
@elseif(al=='/')
inc edx
@endif
@if(edx)
mov byte ptr [esi][ebx],0
@endif
inc ebx
@endw
mov word ptr [esi][ebx],0
add ebx,2
mov byte ptr [esi][ebx],1
xor eax,eax
ld_out:
push eax
invoke CloseHandle,dat_file
pop eax
ret
load_data ENDP
;ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
;Используются при разборе содержимого буфера данных:
;skip_nz - пропуск всех символов, пока не встретится 00h
;skip_nz2 - пропуск всех символов, пока не встретится 0000h
;///////////////////////////////////////////////////////////// Пропуски
skip_nz PROC
@while(byte ptr[esi]!=0)
inc esi
@endw
nc esi
ret
skip_nz ENDP
;=============================================================
skip_nz2 PROC
@while(word ptr[esi]!=0)
inc esi
@endw
add esi,2
ret
skip_nz2 ENDP
;ИЗМЕНЕНИЕ СОЕДИНЕНИЯ
;Функция вызывается при выборе в списке соединений нового соединения
;///////////////////////////////////////////////////////////// Изменение соединения
change_con PROC
;Установка указателя на запись текущего соединения
xor edx,edx ;sub_string
xor ecx,ecx ;cur_con
mov esi,dat_buffer
@while(byte ptr[esi]!=1)
@if(ecx==current_con)
@break
@endif
@if(word ptr[esi]==0)
inc edx
@if(edx==3)
inc ecx
xor edx,edx
@endif
inc esi
@endif
inc esi
@endw
call skip_nz2
;Формирование списка телефонов для данного соединения и установка его позиции
push esi
invoke SendMessageA,phon_window,CB_RESETCONTENT,0,0
pop esi
xor edx,edx;number
@while(byte ptr[esi]!=0)
@push esi,edx
invoke SendMessageA,phon_window,CB_ADDSTRING,0,esi
@pop esi,edx
inc edx
call skip_nz
@endw
inc esi
mov ebx,current_con
@if(con_phone[ebx]>=dl)
mov byte ptr con_phone[ebx],0
@endif
xor eax,eax
mov al,con_phone[ebx]
invoke SendMessageA,phon_window,CB_SETCURSEL,eax,esi
; Формирование списка логинов для данного соединения и установка его позиции
invoke SendMessageA,user_window,CB_RESETCONTENT,0,0
xor edx,edx
@while(byte ptr[esi]!=0)
@push esi,edx
invoke SendMessageA,user_window,CB_ADDSTRING,0,esi
@pop esi,edx
inc edx
call skip_nz
call skip_nz
@endw
mov ebx,current_con
@if(con_user[ebx]>=dl)
mov byte ptr con_user[ebx],0
@endif
xor eax,eax
mov al,con_user[ebx]
invoke SendMessageA,user_window,CB_SETCURSEL,eax,esi
ret
change_con ENDP
;АВАРИЙНОЕ ЗАВЕРШЕНИЕ ПРИЛОЖЕНИЯ
;Вызывается в случае, когда продолжение работы приложения невозможно
;///////////////////////////////////////////////////////////// Фатальный аборт
.const
fatal_caption db "MyCall Error",0
fatal_txt db "Can't register main window class",0
db "Can't create main window",0
db "Can't open mycall.txt",0
db "Can't get size of mycall.txt",0
db "Can't allocate memory for mycall.txt",0
db "Can't read mycall.txt",0
db "Remote Access Service fatal error",0
.code
fatal PROC fatal_code
mov esi,offset fatal_txt
@while(fatal_code)
@while(byte ptr [esi])
inc esi
@endw
inc esi
dec fatal_code
@endw
invoke MessageBoxA,NULL,esi,offset fatal_caption,MB_OK OR MB_ICONERROR
ret
fatal ENDP
;В(Ы)КЛЮЧЕНИЕ ОРГАНОВ УПРАВЛЕНИЯ
;Меняет надпись на кнопке и активизирует списки в зависимости от того,
;находится приложение в режиме выбора или в режиме соединения
;///////////////////////////////////////////////////////////// Выключение органов управления
.const
ec_txt0 db "Call",0
ec_txt1 db "HangUp",0
.code
disable_controls PROC disable
mov eax,win_height
@if(disable)
add eax,12
@endif
invoke SetWindowPos,main_window,NULL,NULL,NULL,win_width,eax,SWP_NOMOVE OR SWP_NOZORDER
@if(disable)
mov eax,offset ec_txt1
@else
mov eax,offset ec_txt0
@endif
invoke SendMessageA,butt_window,WM_SETTEXT,0,eax
mov eax,disable
xor eax,1h
push eax
invoke EnableWindow,conn_window,eax
pop eax
push eax
invoke EnableWindow,phon_window,eax
pop eax
invoke EnableWindow,user_window,eax
ret
disable_controls ENDP
;ДОЗВОН
;///////////////////////////////////////////////////////////// Дозвон
.data?
ras_dial_params RASDIALPARAMS{}
.code
ras_dial PROC
;Подготовка структуры RASDIALPARAMS
mov ras_dial_params.dwSize,sizeof(RASDIALPARAMS)
mov ras_dial_params.szCallbackNumber,0
mov ras_dial_params.szDomain,0
mov esi,dat_buffer
xor ebx,ebx
@while(ebx!=current_con)
xor ecx,ecx
@while(ecx<3)
call skip_nz2
inc ecx
@endw
inc ebx
@endw
mov ras_dial_params.szEntryName,0
push esi
invoke lstrcpy,offset ras_dial_params.szEntryName,esi
pop esi
call skip_nz
mov ras_dial_params.szPhoneNumber,0
push esi
invoke lstrcpy,offset ras_dial_params.szPhoneNumber,esi
pop esi
call skip_nz
dec esi
@while(byte ptr[esi]==0)
inc esi
@endw
xor ecx,ecx
mov ebx,current_con
@while(con_phone[ebx]!=cl)
call skip_nz
inc ecx
@endw
push esi
invoke lstrcat,offset ras_dial_params.szPhoneNumber,esi
pop esi
call skip_nz2
xor ecx,ecx
mov ebx,current_con
@while(con_user[ebx]!=cl)
call skip_nz
call skip_nz
inc ecx
@endw
mov ras_dial_params.szUserName,0
push esi
invoke lstrcat,offset ras_dial_params.szUserName,esi
pop esi
call skip_nz
mov ras_dial_params.szPassword,0
push esi
invoke lstrcat,offset ras_dial_params.szPassword,esi
pop esi
;Дозвон
mov ras_conn,0
invoke RasDialA,0,0,offset ras_dial_params,0,ras_dial_func,offset ras_conn
@if(eax)
call ras_hangup
invoke fatal,FATAL_RAS
invoke ExitProcess,EXIT_RAS_ERROR
@else
mov online,TRUE
@endif
ret
ras_dial ENDP
;ФУНКЦИЯ КОНТРОЛЯ СОСТОЯНИЯ СОЕДИНЕНИЯ
;============================================================= RAS Callback function
.const
ras_state_text db "OpenPort",0
db "PortOpened",0
db "ConnectDevice",0
db "DeviceConnected",0
db "AllDevicesConnected",0
db "Authenticate",0
db "AuthNotify",0
db "AuthRetry",0
db "AuthCallback",0
db "AuthChangePassword",0
db "AuthProject",0
db "AuthLinkSpeed",0
db "AuthAck",0
db "ReAuthenticate",0
db "Authenticated",0
db "PrepareForCallback",0
db "WaitForModemReset",0
db "WaitForCallback",0
db "Projected",0
db "StartAuthentication",0
db "CallbackComplete",0
db "LogonNetwork",0
db "SubEntryConnected",0
db "SubEntryDisconnected",0
db "Interactive",0
db "RetryAuthentication",0
db "CallbackSetByCaller",0
db "PasswordExpired",0
db "Connected",0
db "Disconnected",0
db "Unknown state",0
.data?
ras_monitor_id dd ?
.code
ras_dial_func PROC PUBLIC type_of_event,rasconnstate,ras_error
push esi
@if(online)
;Показ состояния соединения в строке статуса
mov edx,rasconnstate
@if(edx>=RASCS_Connected)
sub edx,RASCS_Connected-28
@else
@if(edx>=RASCS_Interactive)
sub edx,RASCS_Interactive-24
@endif
@endif
@if(edx>30)
mov edx,30
@endif
push edx
mov esi,offset ras_state_text
@while(edx)
call skip_nz
dec edx
@endw
invoke SendMessageA,stat_window,WM_SETTEXT,0,esi
pop edx
;Если соединение установлено - запуск нити контроля
@if(rasconnstate==RASCS_Connected)
invoke CreateThread,NULL,0,offset ras_monitor_func,0,0,offset ras_monitor_id
; Если соединение не установлено - передача сообщения WM_USER для
;повтора дозвона
@elseif(rasconnstate==RASCS_Disconnected)
push edx
invoke PostMessageA,main_window,WM_USER,0,0
pop edx
@endif
@endif
pop esi
ret
ras_dial_func ENDP
;НИТЬ МОНИТОРА РАЗРЫВА
; Каждые 200 мс опрашивает состояние установленного соединения.
;При обнаружении факта разъединения посылает сообщение WM_USER
;и завершается
;============================================================= Нить монитора разрыва
.data?
ras_connect_status RASCONNSTATUS{}
.code
ras_monitor_func PROC PUBLIC param
rm_loop:
@if(online)
mov ras_connect_status.dwSize,sizeof(RASCONNSTATUS)
invoke RasGetConnectStatusA,ras_conn,offset ras_connect_status
@if(!eax)
@if(ras_connect_status.rasconnstate!=RASCS_Disconnected)
invoke Sleep,200
jmp rm_loop
@endif
@endif
@endif
invoke PostMessageA,main_window,WM_USER,0,1
ret
ras_monitor_func ENDP
;ПОЛОЖИТЬ ТРУБКУ
;Если происходит дозвон, или соединение установлено,
;дает команду на разрыв и ожидает, когда RAS ее выполнит
;///////////////////////////////////////////////////////////// Положить трубку
ras_hangup PROC
mov online,FALSE
@if(ras_conn)
invoke RasHangUpA,ras_conn
rh_loop:
mov ras_connect_status.dwSize,sizeof(RASCONNSTATUS)
invoke RasGetConnectStatusA,ras_conn,offset ras_connect_status
@if(eax!=ERROR_INVALID_HANDLE)
invoke Sleep,0
jmp rh_loop
@endif
@endif
mov ras_conn,0
invoke Sleep,1000
ret
ras_hangup ENDP
br>
;#############################################################
end
Main.cpp для mycall (c++)
Это основной файл приложения MyCall на C++. Этот файл в текстовом формате вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файле mycallcb.zip (13192 байта). Имеется также Инструкция программиста.
Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
//Включение заголовочных файлов
//Здесь windows.h и ras.h - стандартные из пакета MS Visual C++,
//а main.h - заголовочный файл приложения MyCall
#include "windows.h"
#include "ras.h"
#include "main.h"
//ГЛАВНАЯ ФУНКЦИЯ ПРИЛОЖЕНИЯ
/////////////////////////////////////////////////////////////// WinMain
int WINAPI WinMain(HINSTANCE hinst,HINSTANCE prev_hinst,LPSTR command_line,int cmd_show){
//Получение дескриптора экземпляра приложения
//Необходимое действие, так как предполагается компиляция приложения без
//подключения runtime-библиотеки. Подробнее...
hinst=GetModuleHandle(NULL);
//Создание главного окна
//Обычное действие, с которого начинаются большинство приложений.
//Единственное отличие в том, что в качестве главного окна в MyCall
//используется окно диалога, описанного в файле ресурсов
//Регистрируется класс главного окна:
WNDCLASSEX mw_class;
mw_class.cbSize=sizeof(WNDCLASSEX);
mw_class.style=NULL;
mw_class.lpfnWndProc=superprocedure;
mw_class.cbClsExtra=0;
mw_class.cbWndExtra=DLGWINDOWEXTRA;
mw_class.hInstance=hinst;
mw_class.hIcon=LoadIcon(hinst,MAKEINTRESOURCE(103));
mw_class.hIconSm=NULL;
mw_class.hCursor=LoadCursor(NULL,IDC_ARROW);
mw_class.hbrBackground=(HBRUSH)COLOR_WINDOW;
mw_class.lpszMenuName=NULL;
mw_class.lpszClassName="MainWindowClass";
if(!RegisterClassEx(&mw_class)){fatal(FATAL_MAIN_CLASS_REG);return EXIT_COMMON_ERROR;}
//Главное окно создается:
if(!(main_window=CreateDialog(hinst,MAKEINTRESOURCE(101),NULL,NULL))){fatal(FATAL_MAIN_CLASS_CREATE);return EXIT_COMMON_ERROR;}
// По состоянию списка conn_window формируются остальные списки
change_con();//
//ЦИКЛ ОЖИДАНИЯ СООБЩЕНИЙ
//Неотъемлемый элемент приложений для Windows. В MyCall никаких особенностей не имеет
MSG loop_message;
while (GetMessage(&loop_message,NULL,0,0)){TranslateMessage(&loop_message);DispatchMessage(&loop_message);}
//ЗАВЕРШЕНИЕ РАБОТЫ ПРИЛОЖЕНИЯ
//Поскольку MyCall собирается без runtime-библиотеки, для завершения работы
//обязательно использовать функцию ExitProcess, при этом оператор return оказывается
//недостижимым, но необходим по требованиям синтаксиса. Подробнее...
ExitProcess(loop_message.wParam);
return 0;
}
//ОКОННАЯ ПРОЦЕДУРА
//Стандартный элемент приложений для Windows. В MyCall особенностей не имеет.
/////////////////////////////////////////////////////////////// Оконная процедура
LRESULT CALLBACK superprocedure(HWND window_from,UINT message,WPARAM w_param,LPARAM l_param){
//Разбор и обработка оконных сообщений
switch(message){
//Обработка сообщения WM_COMMAND, передаваемого элементами управления диалога
case WM_COMMAND:
//Обработка уведомления CBN_SELCHANGE, передаваемого списками при изменении позиции:
//1000: список соединений conn_window. Изменяет содержание phon_window и user_window
//1001: список телефонов phon_window. Устанавливает новый телефон для текущего соединения con_phone[current_con]
//1002: список логинов user_window. Устанавливает новый логин для текущего соединения con_user[current_con]
if(HIWORD(w_param)==CBN_SELCHANGE){
switch (LOWORD(w_param)){
case 1000:
current_con=SendMessage(conn_window,CB_GETCURSEL,0,0);
if(current_con==CB_ERR){current_con=0;}
change_con();
return 0;
case 1001:
con_phone[current_con]=SendMessage(phon_window,CB_GETCURSEL,0,0);
if(con_phone[current_con]==CB_ERR){con_phone[current_con]=0;}
return 0;
case 1002:
con_user[current_con]=SendMessage(user_window,CB_GETCURSEL,0,0);
if(con_user[current_con]==CB_ERR){con_user[current_con]=0;}
return 0;
default:
break;
}
return 0;
}
// Обработка уведомления BN_CLICKED, передаваемого кнопкой при клике:
//в состоянии online=TRUE прекращает дозвон (разрывает соединение) и включает списки
//в состоянии online=FALSE отключает списки и начинает дозвон
if((HIWORD(w_param)==BN_CLICKED)&&(LOWORD(w_param))==1003){
if(online){
EnableWindow(butt_window,FALSE);
ras_hangup();
EnableWindow(butt_window,TRUE);
disable_controls(FALSE);
}else{
disable_controls(TRUE);
ras_dial();
}
return 0;
}
break;
//Обработка сообщения WM_USER, используемого в MyCall нитью монитора разрыва.
//Обнаружив факт разрыва соединения, нить монитора передает WM_USER. Если был коннект,
//то включаются списки, в противном случает выполняется повторный дозвон
case WM_USER:
ras_hangup();
l_param?disable_controls(FALSE):ras_dial();
return 0;
//Обработка сообщения WM_MOVE: запоминание новой позиции окна
//для последующей записи ее в файл mycall.ini
case WM_MOVE:
RECT win_pos;
if(GetWindowRect(main_window,&win_pos)){
main_win_left=win_pos.left;
main_win_top=win_pos.top;
/div>
}else{
main_win_left=(int)LOWORD(l_param);
main_win_top=(int)HIWORD(l_param);
}
return 0;
//При завершении работы приложения записать файл mycall.ini,
//и освободить память
case WM_DESTROY:
save_ini();
if(dat_buffer){GlobalFree(dat_buffer);}
PostQuitMessage(EXIT_NORMAL);
return 0;
//При закрытии главного окна заблокировать кнопку
//и разорвать соединение
case WM_CLOSE:
EnableWindow(butt_window,FALSE);
if(online){ras_hangup();}
break;
}
//Обработка глобального оконного сообщения, используемого для
//взаимодействия экземпляров приложения. Получив это сообщение,
//экземпляр, запущенный первым, сообщает дескриптор своего главного окна.
//Сообщение, полученное вторым, принимает от первого дескриптор его главного окна,
//выводит его на первый план и завершается. Подробнее...
if(message==my_message){
if((HWND)w_param!=main_window){
if(l_param==0){
SendMessage(HWND_BROADCAST,my_message,(WPARAM)main_window,1);
}else{
SetForegroundWindow((HWND)w_param);
ExitProcess(EXIT_OVERLOADED);
}
}
return 0;
}
//Возврат необработанных сообщений системе
return DefWindowProc(window_from,message,w_param,l_param);
}
//ЗАГРУЗКА ФАЙЛА ИНИЦИАЛИЗАЦИИ
//Файл mycall.ini хранит состояние списков и положение главного окна на момент
//завершения предыдущего сеанса работы приложения
/////////////////////////////////////////////////////////////// Загрузка файла инициализации
void load_ini(){
//Инициализация глобальных переменных
main_win_left=MAIN_WIN_DEFAULT_LEFT;
main_win_top=MAIN_WIN_DEFAULT_TOP;
current_con=0;
for(int i=0;i
con_phone[i]=0;
con_user[i]=0;
}
//Попытка загрузки файла mycall.ini
//В случае неудачи используются значения по умолчанию
HANDLE ini_file=CreateFile(INI_FILE,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(ini_file!=INVALID_HANDLE_VALUE){
//Чтение файла mycall.ini
BYTE buffer[INI_FILE_LENGTH];
DWORD bytes_read;
if(ReadFile(ini_file,buffer,INI_FILE_LENGTH,&bytes_read,NULL)){
//Приведение позиции окна к фактическим размерам экрана и запоминание
main_win_left=(((INT)buffer[1])<<8)|((INT)buffer[0]);
if(main_win_left>=GetSystemMetrics(SM_CXSCREEN)-10){main_win_left=MAIN_WIN_DEFAULT_LEFT;}
main_win_top=(((INT)buffer[3])<<8)|((INT)buffer[2]);
if(main_win_top>=GetSystemMetrics(SM_CYSCREEN)-10){main_win_top=MAIN_WIN_DEFAULT_TOP;}
//Запоминание позиций списков телефонов и логинов
current_con=(INT)buffer[4];
for(int i=0;i
con_phone[i]=(INT)buffer[(i<<1)+5];
con_user[i]=(INT)buffer[(i<<1)+6];
}
}
CloseHandle(ini_file);
}
}
//СОХРАНЕНИЕ ФАЙЛА ИНИЦИАЛИЗАЦИИ mycall.ini
//В файле сохраняются положение главного окна на экране и позиции
//списков на момент завершения работы приложения
/////////////////////////////////////////////////////////////// Сохранение файла инициализации
void save_ini(){
HANDLE ini_file=CreateFile(INI_FILE,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if(ini_file!=INVALID_HANDLE_VALUE){
BYTE buffer[INI_FILE_LENGTH];
buffer[0]=(BYTE)main_win_left;
buffer[1]=(BYTE)(main_win_left>>8);
buffer[2]=(BYTE)main_win_top;
buffer[3]=(BYTE)(main_win_top>>8);
buffer[4]=(BYTE)current_con;
for(int i=0;i
buffer[(i<<1)+5]=(BYTE)con_phone[i];
buffer[(i<<1)+6]=(BYTE)con_user[i];
}
DWORD bytes_written;
WriteFile(ini_file,buffer,INI_FILE_LENGTH,&bytes_written,NULL);
CloseHandle(ini_file);
}
}
//ЗАГРУЗКА ФАЙЛА ДАННЫХ mycall.dat
// Файл содержит имена соединений, телефоны и логины
/////////////////////////////////////////////////////////////// Загрузка файла данных
BOOL load_data(){
//Открытие файла, подготовка буфера для него и считывание файла в буфер
HANDLE dat_file=CreateFile(DAT_FILE,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(dat_file==INVALID_HANDLE_VALUE){fatal(FATAL_DAT_FILE_OPEN);return FALSE;}
DWORD dat_file_size=GetFileSize(dat_file,NULL);
if(dat_file_size==0xffffffff){CloseHandle(dat_file);fatal(FATAL_DAT_FILE_SIZE);return FALSE;}
dat_buffer=(LPBYTE)GlobalAlloc(GMEM_FIXED,dat_file_size+3);
if(dat_buffer==NULL){CloseHandle(dat_file);fatal(FATAL_DAT_BUF_ALLOC);return FALSE;}
DWORD bytes_read;
if(!ReadFile(dat_file,dat_buffer,dat_file_size,&bytes_read,NULL)){CloseHandle(dat_file);fatal(FATAL_DAT_FILE_READ);return FALSE;}
CloseHandle(dat_file);
//Замена слэшей, CR, LF и пробелов на 0h, добавление в конец 1h
ptr=dat_buffer;
for(DWORD i=0;i
if((*ptr==0xd)||(*ptr==0xa)||(*ptr=='/')||(*ptr==' ')){*ptr=0;}
ptr++;}
dat_buffer[i]=0;dat_buffer[i+1]=0;dat_buffer[i+2]=1;
return TRUE;
}
//ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
//Используются при разборе содержимого буфера данных:
//skip_nz - пропуск всех символов, пока не встретится 00h
//skip_nz2 - пропуск всех символов, пока не встретится 0000h
/////////////////////////////////////////////////////////////// Пропуски
VOID skip_nz(){
while(*ptr!=0){ptr++;}
ptr++;
}
VOID skip_nz2(){
while(*(LPWORD)ptr!=0){ptr++;}
ptr+=2;
}
//ИЗМЕНЕНИЕ СОЕДИНЕНИЯ
//Функция вызывается при выборе в списке соединений нового соединения
/////////////////////////////////////////////////////////////// Изменение соединения
VOID change_con(){
// Установка указателя на запись текущего соединения
INT sub_string=0;
INT cur_con=0;
ptr=dat_buffer;
while(*ptr!=1){
if(cur_con==current_con){break;}
if(*(LPWORD)ptr==0){
if(++sub_string==3){cur_con++;sub_string=0;}
ptr++;
}
ptr++;
}
skip_nz2();
//Формирование списка телефонов для данного соединения и установка его позиции
SendMessage(phon_window,CB_RESETCONTENT,0,0);
int number=0;
while(*ptr!=0){
SendMessage(phon_window,CB_ADDSTRING,0,(LPARAM)ptr);
number++;
skip_nz();
}
ptr++;
if(con_phone[current_con]>=number){con_phone[current_con]=0;}
SendMessage(phon_window,CB_SETCURSEL,(WPARAM)con_phone[current_con],0);
//Формирование списка логинов для данного соединения и установка его позиции
SendMessage(user_window,CB_RESETCONTENT,0,0);
number=0;
while(*ptr!=0){
SendMessage(user_window,CB_ADDSTRING,0,(LPARAM)ptr);
number++;
skip_nz();
skip_nz();
}
if(con_user[current_con]>=number){con_user[current_con]=0;}
SendMessage(user_window,CB_SETCURSEL,(WPARAM)con_user[current_con],0);
}
//АВАРИЙНОЕ ЗАВЕРШЕНИЕ ПРИЛОЖЕНИЯ
//Вызывается в случае, когда продолжение работы приложения невозможно
/////////////////////////////////////////////////////////////// Фатальный аборт
void fatal(int fatal_code){
LPCSTR fatal_text[]={
"Can't register main window class",
"Can't create main window",
"Can't open mycall.txt",
"Can't get size of mycall.txt",
"Can't allocate memory for mycall.txt",
"Can't read mycall.txt",
"Remote Access Service fatal error"};
MessageBox(NULL,fatal_text[fatal_code],"MyCall Error",MB_OK|MB_ICONERROR);
}
//В(Ы)КЛЮЧЕНИЕ ОРГАНОВ УПРАВЛЕНИЯ
//Меняет надпись на кнопке и активизирует списки в зависимости от того,
//находится приложение в режиме выбора или в режиме соединения
/////////////////////////////////////////////////////////////// В(ы)ключение органов управления
VOID disable_controls(BOOL disable){
SetWindowPos(main_window,NULL,NULL,NULL,win_width,win_height+(disable?12:0),SWP_NOMOVE|SWP_NOZORDER);
SendMessage(butt_window,WM_SETTEXT,0,(LPARAM)(disable?"HangUp":"Call"));
EnableWindow(conn_window,!disable);
EnableWindow(phon_window,!disable);
EnableWindow(user_window,!disable);
}
//ДОЗВОН
/////////////////////////////////////////////////////////////// Дозвон
VOID ras_dial(){
//Подготовка структуры RASDIALPARAMS
RASDIALPARAMS ras_dial_params;
ras_dial_params.dwSize=sizeof(RASDIALPARAMS);
ras_dial_params.szCallbackNumber[0]=0;
ras_dial_params.szDomain[0]=0;
ptr=dat_buffer;
for(int j=0;j!=current_con;j++){for(int k=0;k<3;k++){skip_nz2();}}
ras_dial_params.szEntryName[0]=0;
lstrcpy(ras_dial_params.szEntryName,(LPSTR)ptr);
skip_nz();
ras_dial_params.szPhoneNumber[0]=0;
lstrcpy(ras_dial_params.szPhoneNumber,(LPSTR)ptr);
skip_nz();ptr--;
while(*ptr==0){ptr++;}
for(j=0;j!=con_phone[current_con];j++){skip_nz();}
lstrcat(ras_dial_params.szPhoneNumber,(LPSTR)ptr);
skip_nz2();
for(j=0;j!=con_user[current_con];j++){skip_nz();skip_nz();}
ras_dial_params.szUserName[0]=0;
lstrcpy(ras_dial_params.szUserName,(LPSTR)ptr);
skip_nz();
ras_dial_params.szPassword[0]=0;
lstrcpy(ras_dial_params.szPassword,(LPSTR)ptr);
//Дозвон
ras_conn=NULL;
if(RasDial(0,0,&ras_dial_params,0,ras_dial_func,&ras_conn)){
ras_hangup();
fatal(FATAL_RAS);
ExitProcess(EXIT_RAS_ERROR);
}else{
online=TRUE;
}
}
//ФУНКЦИЯ КОНТРОЛЯ СОСТОЯНИЯ СОЕДИНЕНИЯ
/////////////////////////////////////////////////////////////// Контроль состояния соединения
VOID WINAPI ras_dial_func(UINT msg,RASCONNSTATE rasconnstate,DWORD error){
LPCSTR ras_state_text[]={
"OpenPort",
"PortOpened",
"ConnectDevice",
"DeviceConnected",
"AllDevicesConnected",
"Authenticate",
"AuthNotify",
"AuthRetry",
"AuthCallback",
"AuthChangePassword",
"AuthProject",
"AuthLinkSpeed",
"AuthAck",
"ReAuthenticate",
"Authenticated",
"PrepareForCallback",
"WaitForModemReset",
"WaitForCallback",
"Projected",
"StartAuthentication",
"CallbackComplete",
"LogonNetwork",
"SubEntryConnected",
"SubEntryDisconnected",
"Interactive",
"RetryAuthentication",
"CallbackSetByCaller",
"PasswordExpired",
"Connected",
"Disconnected",
"Unknown state"};
if(online){
//Показ состояния соединения в строке статуса
UINT ras_message_num=rasconnstate;
if(ras_message_num>=RASCS_Connected){ras_message_num-=RASCS_Connected-28;}
else{if(ras_message_num>=RASCS_Interactive){ras_message_num-=RASCS_Interactive-24;}}
if(ras_message_num>30){ras_message_num=30;}
SendMessage(stat_window,WM_SETTEXT,0,(LPARAM)ras_state_text[ras_message_num]);
//Если соединение установлено - запуск нити контроля
if(rasconnstate==RASCS_Connected){
CreateThread(NULL,0,ras_monitor_func,0,0,&ras_monitor_id);
//Если соединение не установлено - передача сообщения WM_USER для
//повтора дозвона
}else{
if(rasconnstate==RASCS_Disconnected){
PostMessage(main_window,WM_USER,0,0);
}
}
}
}
//НИТЬ МОНИТОРА РАЗРЫВА
// Каждые 200 мс опрашивает состояние установленного соединения.
//При обнаружении факта разъединения посылает сообщение WM_USER
//и завершается
//============================================================= Нить монитора разрыва
DWORD WINAPI ras_monitor_func(LPVOID param){
RASCONNSTATUS ras_connect_status;
while(online){
ras_connect_status.dwSize=sizeof(RASCONNSTATUS);
if(RasGetConnectStatus(ras_conn,&ras_connect_status)){break;}
if(ras_connect_status.rasconnstate==RASCS_Disconnected){break;}
Sleep(200);
}
PostMessage(main_window,WM_USER,0,1);
return 0;
}
//ПОЛОЖИТЬ ТРУБКУ
//Если происходит дозвон, или соединение установлено,
//дает команду на разрыв и ожидает, когда RAS ее выполнит
/////////////////////////////////////////////////////////////// Положить трубку
VOID ras_hangup(){
RASCONNSTATUS ras_connect_status;
online=FALSE;
if(ras_conn){
RasHangUp(ras_conn);
while(TRUE){
ras_connect_status.dwSize=sizeof(RASCONNSTATUS);
if(RasGetConnectStatus(ras_conn,&ras_connect_status)==ERROR_INVALID_HANDLE){break;}
Sleep(0);
}
ras_conn=0;
Sleep(1000);
}
}
Main.h для mycall (c++)
Это файл заголовков для приложения MyCall на C++. Этот файл в текстовом формате вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файле mycallcb.zip (13192 байта). Имеется также Инструкция программиста.
Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
//Положение главного окна на экране по умолчанию (когда mycall.ini недоступен)
#define MAIN_WIN_DEFAULT_LEFT 100
#define MAIN_WIN_DEFAULT_TOP 100
//Максимальное количество позиций в списках
#define MAX_CON 16
//Размер файла mycall.ini
#define INI_FILE_LENGTH 4+1+MAX_CON*2
//Коды выхода приложения:
//EXIT_NORMAL: нормальное завершение работы
//EXIT_COMMON_ERROR: фатальная ошибка
//EXIT_OVERLOADED: экземпляр приложения уже существует
//EXIT_RAS_ERROR: ошибка Remote Access Service
#define EXIT_NORMAL 0
#define EXIT_COMMON_ERROR 1
#define EXIT_OVERLOADED 2
#define EXIT_RAS_ERROR 3
//Коды фатальных ошибок
#define FATAL_MAIN_CLASS_REG 0
#define FATAL_MAIN_CLASS_CREATE 1
#define FATAL_DAT_FILE_OPEN 2
#define FATAL_DAT_FILE_SIZE 3
#define FATAL_DAT_BUF_ALLOC 4
#define FATAL_DAT_FILE_READ 5
#define FATAL_RAS 6
//Имена используемых файлов
#define INI_FILE "mycall.ini"
#define DAT_FILE "mycall.txt"
//Объявления функций
LRESULT CALLBACK superprocedure (HWND,UINT,WPARAM,LPARAM);
VOID load_ini();
VOID save_ini();
BOOL load_data();
VOID change_con();
VOID fatal(INT);
VOID disable_controls(BOOL);
VOID ras_dial();
VOID ras_hangup();
VOID ras_control();
VOID WINAPI ras_dial_func(UINT,RASCONNSTATE,DWORD);
DWORD WINAPI ras_monitor_func(LPVOID);
//Дескрипторы окон и элементов управления
HWND main_window;
HWND conn_window;
HWND phon_window;
HWND user_window;
HWND butt_window;
HWND stat_window;
//Положение и размеры главного окна
INT main_win_left;
INT main_win_top;
LONG win_width;
LONG win_height;
//Текущее соединение
INT current_con;
//Массивы телефонов и логинов для текущего соещинения
INT con_phone[MAX_CON];
INT con_user[MAX_CON];
//Дескриптор установленного соединения
HRASCONN ras_conn;
//Идентификатор нити монитора разрыва
DWORD ras_monitor_id;
//Код регистрируемого глобального оконного сообщения "MyCallMessage"
UINT my_message;
//Флаг состояния приложения:
//FALSE: выбор соединения пользователем
//TRUE: выполняется дозвон или поддерживается коннект
BOOL online=FALSE;
//Буфер для mycall.txt
LPBYTE dat_buffer=0;
//Указатель внутри этого буфера
LPBYTE ptr;
Main.inc для mycall (ассемблер)
Это файл заголовков для приложения MyCall на ассемблере. Этот файл в текстовом формате вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файле mycallab.zip (15913 байт). Имеется также Инструкция программиста.
Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
;КОНСТАНТЫ
//Положение главного окна на экране по умолчанию (когда mycall.ini недоступен)
MAIN_WIN_DEFAULT_LEFT=100
MAIN_WIN_DEFAULT_TOP=100
//Максимальное количество позиций в списках
MAX_CON=16
//Размер файла mycall.ini
INI_FILE_LENGTH=4+1+MAX_CON*2
//Коды выхода приложения:
//EXIT_NORMAL: нормальное завершение работы
//EXIT_COMMON_ERROR: фатальная ошибка
//EXIT_OVERLOADED: экземпляр приложения уже существует
//EXIT_RAS_ERROR: ошибка Remote Access Service
EXIT_NORMAL=0
EXIT_COMMON_ERROR=1
EXIT_OVERLOADED=2
EXIT_RAS_ERROR=3
//Коды фатальных ошибок
FATAL_MAIN_CLASS_REG=0
FATAL_MAIN_CLASS_CREATE=1
FATAL_DAT_FILE_OPEN=2
FATAL_DAT_FILE_SIZE=3
FATAL_DAT_BUF_ALLOC=4
FATAL_DAT_FILE_READ=5
FATAL_RAS=6
;ПРОТОТИПЫ ПОЛЬЗОВАТЕЛЬСКИХ ПРОЦЕДУР
;Необходимы в случаях, когда процедура вызывается оператором INVOKE,
;или когда ее адрес передается системной функции API в качестве параметра
fatal PROTO :DWORD
ras_dial_func PROTO :DWORD,:DWORD,:DWORD
ras_monitor_func PROTO :DWORD
;ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
//Дескрипторы окон и элементов управления
main_window dd ?
conn_window dd ?
phon_window dd ?
user_window dd ?
butt_window dd ?
stat_window dd ?
//Положение и размеры главного окна
main_win_left dd ?
main_win_top dd ?
win_width dd ?
win_height dd ?
//Текущее соединение
current_con dd ?
//Массивы телефонов и логинов для текущего соещинения
con_phone db MAX_CON dup(?)
con_user db MAX_CON dup(?)
//Дескриптор установленного соединения
ras_conn dd ?
//Код регистрируемого глобального оконного сообщения "MyCallMessage"
my_message dd ?
//Флаг состояния приложения:
//FALSE: выбор соединения пользователем
//TRUE: выполняется дозвон или поддерживается коннект
online dd ?
//Буфер для mycall.txt
dat_buffer dd ?
Минимальная stub-программа
"Stub" в переводе с английского - "пень, обломок, огрызок". Это в литературном переводе. В нелитературный перевод углубляться не будем. Скажем только, что это то самое, что показывает компьютер человеку, попытавшемуся запустить из-под DOS приложение, написанное для Windows. Обычно он показывает сообщение: "This program cannot be run in DOS mode". А вы что подумали?
В связи с понятием "stub-программа" обычно возникают три вопроса:
Как она работает? Чтобы ответить на этот вопрос, следует заглянуть во внутренности формата PE-файла - стандартного исполняемого модуля Windows. Там мы обнаружим следующее:
PE-формат - не единственный формат исполняемых модулей, который понимает Windows. Но мы не станем здесь рассматривать другие форматы, так как применительно к теме статьи это не имеет смысла.
Глупая DOS загружает exe-файл, обнаруживает в его начале привычный для себя заголовок и исполняет stub-программу, ничего не зная о наличии в файле еще чего-то, кроме слов "а пошел ты туда-то и туда-то".
Подозреваем, что не все компоновщики поддерживают эту опцию. Однако link.exe из состава MS Developer Studio, конечно же, поддерживает.
Написанную stub-программу используйте на этапе компоновки своего Windows-приложения. Для этого в командную строку компоновщика добавьте опцию /STUB:"filename.exe", где filename.exe, как вы можете догадаться - это имя вашей stub-программы, при необходимости с путем. Обнаружив эту опцию, компоновщик заменит стандартную stub-программу на вашу.
Здесь настоящего ассемблерщика подстерегает серьезное разочарование. Проблема в следующем. Обычно при компоновке exe-программ для DOS в исполняемом файле резервируется довольно большое место под таблицу перемещения. Например, в порядке вещей, если таблица перемещения вольготно располагается между смещениями 1ch (конец заголовка) и 200h (начало сегмента кода). Даже если ваша stub-программа не содержит ни одного фрагмента, подлежащего перемещению, все равно 486 байт чистых нулей будет бессмысленно вбацано в нежное девственное тело вашего Windows-приложения. Задачка, стоит ли избегать такого варварского разбазаривания дискового пространства и как это сделать, предлагается для самостоятельного решения. Кое-какие идеи читайте далее.
Минимальная stub-программа. Заглянув внутрь исполняемого модуля какого-нибудь Windows-приложения, вы обнаружите, что стандартная stub-программа чаще всего занимает 128 байт. Можно ли уменьшить этот размер и до какой величины? Отвечаем: можно, до 64 байт.
Возьмите любой редактор бинарных файлов и создайте с его помощью вот такой файл:
000000 4D 5A 00 00 01 00 00 00 02 00 00 00 FF FF 00 00
000010 40 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000020 B4 4C CD 21 00 00 00 00 00 00 00 00 00 00 00 00
000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Присвойте ему имя, например, stub.exe и используйте в качестве stub-программы. А если лень набивать самому - можете скачать его отсюда.
Вот расшифровка содержимого файла stub.exe с комментариями:
| смещение | значение | назначение | комментарий |
| +0 | 5A4D | Подпись exe-файла ('MZ') | |
| +2 | 0000 | Длина последней неполной страницы образа, байт | Игнорируется операционной системой |
| +4 | 0001 | Длина образа, страниц (страница = 512 байт) | Программа занимает менее одной страницы |
| +6 | 0000 | Число элементов в таблице перемещения | В этой программе перемещаемых элементов нет |
| +8 | 0002 | Размер exe-заголовка, параграфов (параграф = 16 байт) | Указывается размер базовой части заголовка. С учетом остальных значений параметров в данном случае означает, что исполняемый код в файле начинается со смещения 20h, и стартовый адрес находится в начале исполняемого кода |
| +0ah | 0000 | Минимум требуемой памяти за концом программы (параграфов) | В данном случае смысла не имеет |
| +0ch | FFFF | Максимум требуемой памяти за концом программы (параграфов) | Традиционно отводится вся доступная память |
| +0eh | 0000 | Сегментное смещение сегмента стека (для установки регистра ss) | |
| +10h | 0040 | Значение регистра sp (указателя стека) при запуске | В этой программе стек не имеет значения |
| +12h | 0000 | Контрольная сумма исполняемого модуля | Не используется |
| +14h | 0000 | Значение регистра ip (указателя команд) при запуске | Стартовая точка совпадает с началом кодового сегмента |
| +16h | 0000 | Cегментное смещение кодового сегмента (для установки регистра cs) | |
| +18h | 0040 | Cмещение в файле 1-го элемента перемещения | В этой программе ни одного элемента перемещения нет, а исполняемый код находится внутри заголовка, поэтому данное значение совпадает с концом программы |
| +1ah | 0000 | Номер оверлея | Здесь не используется |
| +1eh | 0000 0000 |
Резерв - 4 байта | |
| +20h | 4CB4 21CD |
Исполняемый код: mov ah,4ch int 21h |
В данном случае программа просто возвращает управление операционной системе. Максимальный размер исполняемого кода для данных значений заголовка - 28 байт (смещения 20h...3bh). Обратите внимание, что исполняемый код с целью экономии размера программы находится в области, отведенной под exe-заголовок! |
| +3ch | 0000 0000 |
Зарезервированное двойное слово | Используется компоновщиком Windows-приложения для размещения смещения PE-заголовка |
Вероятно, уменьшить размер stub- программы до величины, меньшей 64 байт, невозможно. А вот интересный вопрос: возможно ли вообще изъять stub-программу из модуля? Не спешите отвечать "нет"! 16-битные компоновщики от Microsoft имели в синтаксисе def-файла конструкцию вида STUB NONE, которая, согласно документации, предназначалась для использования при сборке dll-библиотек. В 32-битных компоновщиках эта возможность пропала. А жаль...
Вот уж в самом деле, "никогда не говори "никогда"! 24 июля 2001 года, в день падения 114-летнего рекорда жары в Москве, пришло письмо:
|
На вашем сайте assembler.ru в статье "Минимальная stub-программа" автором упомянуто, что создать stub короче 40h байт, вероятнее всего, невозможно. Привожу пример stub'а длиной в 20h байт: 00000000 4D 5A 00 00 01 00 00 00 01 00 00 00 FF FF 00 00 MZ.............. 00000010 B4 4C CD 21 00 00 00 00 40 00 00 00 00 00 00 00 .L.!....@....... В отличие от примера, приведенного в статье, размер заголовка уменьшен до одного параграфа. В терминах статьи это означает, что stub-код располагается по смещению 10h от начала файла. Очевидно, после такого "сокращения" области заголовка от 10h и выше продолжают использоваться. Но в данном случае это неважно, так как на месте 4CB4h находится initial SP, а на месте 21CDh - checksum. Ни то, ни другое роли не играют. К сожалению, stub-код ограничен в 4 байта, потому что уже следующий word - initial IP. Далее, в файле со stub'ом в 20h байт смещение 3Ch от начала файла приходится на смещение 1Ch PE-заголовка. Здесь расположен dword, который, по-видимому, ни на что не влияет, так что можно спокойно ставить туда 00000020h: 00000020 50 45 00 00 4C 01 07 00 21 B1 97 36 00 00 00 00 PE..L...!..6.... 00000030 00 00 00 00 E0 00 0E 01 0B 01 04 14 20 00 00 00 ............ ... Такую штуку вряд ли поддерживают компоновщики (мне от них этого добиться не удалось), я лично правил EXE вручную. Зато работает. Напоследок - пример тоже рабочего stub'а длиной в 18h байт - меньше, по-моему, некуда: 00000000 4D 5A 00 00 01 00 00 00 00 00 00 00 B0 21 CD 29 MZ...........!.) 00000010 B4 4C CD 21 0C 00 00 00 50 45 00 00 4C 01 07 00 .L.!....PE..L... 00000020 21 B1 97 36 00 00 00 00 00 00 00 00 E0 00 0E 01 !..6............ 00000030 0B 01 04 14 20 00 00 00 00 2E 00 00 18 00 00 00 .... ........... Из-под DOS он выводит восклицательный знак (21h). Ну как, размер достоин настоящего ассемблерщика? автор - Grief (soul_inspector@chat.ru) |
Действительно, ни отнять, ни прибавить: только в голову настоящего ассемблерщика могут придти трюки, позволяющие так, чтобы помягче сказать, использовать формат исполняемого файла. Еще раз, вот они:
Конечно же, заставить какой-нибудь компоновщик создать исполняемый файл с предлагаемыми stub'ами не удастся. Такие трюки придется делать вручную, с помощью какого-нибудь бинарного редактора: переместить образ приложения внутри файла на нужное смещение, отредактировать exe-заголовок, PE-заголовок, заголовки секций - ну, в общем, кто знает формат - тот справится. (А лучше доверить эту рутину какой-нибудь маленькой программуле.)
Ну и напоследок, опровергнув свои же слова что "...меньше некуда...", Grief прислал вообще шедевр - вполне работающий stub длиной всего 0Ch!!! Разобраться, как он устроен, вы сможете самостоятельно, руководствуясь уже достаточно ясной идеей: в заголовках полным-полно никому не нужных полей, которые мы можем использовать для своих целей. Итак:
00000000 4D 5A 00 00 01 00 00 00 01 00 00 00 50 45 00 00 MZ..........PE..
00000010 4C 01 07 00 08 00 00 00 B0 21 CD 29 B4 4C CD 21 L........!.).L.!
00000020 E0 00 0E 01 0B 01 04 14 20 00 00 00 00 2E 00 00 ........ .......
00000030 18 00 00 00 00 E0 00 00 00 10 00 00 0C 00 00 00 ................
Минимальное приложение
Вопрос о минимальной программе для того или иного языка программирования и операционной среды обычно возникает в следующих случаях:
Между тем вопрос о минимальной программе - совсем не простейший, и представляет отнюдь не академический интерес. Минимальная программа, очевидно, решает две задачи: (1)стартует и (2)завершается в конкретной рабочей среде. И то, и другое она должна делать корректно, с тем, чтобы:
В результате компиляции и сборки этого приложения получается исполняемый файл размером 1536 байт. Из них собственно код занимает 10 байт. Все остальное - на совести создателей формата PE-файла. Приложение безропотно запускается и не задумываясь завершает свою работу, возвращая код выхода.
Что здесь что и зачем?
Замечание 1. По поводу необходимости применения функции ExitProcess. Именно с ее помощью, а не посредством команды ret, как могли бы ожидать знатоки C/C++, должно завершаться приложение win32, написанное на ассемблере.
Если залезть внутрь runtime-библиотеки C/C++, то мы увидим, что именно так завершает работу приложения уже упоминавшаяся функция _WinMainCRTStartup. Но поскольку подключать runtime-библиотеку C/C++ к ассемблерной программе как-то нелогично (хотя и вполне возможно), мы должны вызвать ExitProcess "вручную". Только эта функция корректно завершает работу приложения, в частности, оповещая использовавшиеся приложением библиотеки DLL о необходимости декрементировать их счетчики занятости и, при обнулении последних, выгрузиться из памяти.
Замечание 2. А вот и обещанная плохая новость про параметры стартовой процедуры. Дело в том, что установкой их значений занимается - кто бы вы думали? - опять же _WinMainCRTStartup! И следовательно, в ассемблерных программах, где ее нет, при входе в WinMain параметры содержат:
Так что, воленс-ноленс, придется получать эту ценную информацию самостоятельно. Как говорится, клик хере.
Замечание 3. И все-таки приведенный текст приложения - не самый минимальный. Можно сделать его еще меньше, для чего следует убрать все параметры функции WinMain. Поскольку приложение собирается без runtime-библиотеки C/C++, это вполне допустимо, так как больше не с чем выполнять ее связывание. В результате объем исполняемого файла останется тем же, а вот объем исполняемого кода в нем (образа приложения) сократится аж до 7 байт. Про это стоит почитать еще.
Замечание 4. В этой статье рассматривается минимизация размера кода программы, но отнюдь не exe-файла, ее содержащего. Как известно, исполняемые файлы Windows обычно имеют так называемый PE-формат, и манипуляции с этим форматом дают некоторые возможности для сокращения размера файла. Например, можно вместо стандартной stub-программы использовать более компактную, собственной разработки.
Ms devstudio - среда разработки asm
Настоящие ассемблерщики - народ неприхотливый, хорошая находка для рачительной хозяйки. Едят, как правило, мало, и не особо разбираясь, что (одного моего друга жена, большая любительница животных, накормила как-то ради эксперимента педигрипалом. Очень хвалил). Место в помещении почти не занимают: не более двух кв.м вместе с компьютером. Линяют не чаще раза в год. В спячку, правда, не впадают, зато гон непродолжительный и тихий. В совокупности представляют собой прекрасный вторичный рынок для компьютеров 386 серии. Основой операционной среды предпочитают видеть кастрированный по самое ядро Norton Commander 5.0. Трансляцию запускают из командной строки (самые волевые с трудом заставляют себя написать batch-файл). Отладчик при разработке собственных приложений вообще не используют.
Поэтому Microsoft Developer Studio для них - как стадион "Уэмбли" для единственной выжившей после зимовки коровы симментальской породы из колхоза "Привет коммунизму": гораздо больше, чем нужно. И на такие его (MS DevStudio, а не "Уэмбли") мелкие недостатки, как отсутствие поддержки ассемблера, пенять не приходится.
Впрочем, к делу. Первый шаг - это подготовка MS DevStudio для работы с MASM:
Ваш пакет MS Developer Studio готов к разработке приложений win32 на MASM.
Любознательный читатель спросит: почему бы не использовать для разработки приложений inline-ассемблер, являющийся, как известно, неотъемлемым атрибутом C++. Вообще-то ничего невозможного на свете нет. Можно и отверткой в ухе ковыряться, только не очень глубоко. Но надо помнить, что inline-ассемблер предназначен для улучшения языка C++, а не для разработки самостоятельных приложений. Поэтому из него исключены большинство директив и, самое обидное, все макросредства, сильно помогающие облегчить процесс программирования.
Следующий шаг - это подготовка включаемых файлов:
Файл @struct.inc - это авторская отсебятина, которой в принципе можно и не пользоваться, а можно и наоборот, добавить в него что-нибудь свое, полезное. Что и для чего в этом файле, вы можете прочитать здесь.
С файлом windows.inc все гораздо сложнее. Это файл заголовков API, ассемблерный аналог той тучи h-файлов, которые содержатся в пакете Visual C++ и начинаются с файла windows.h (и, возможно, не только). Поскольку Microsoft официально не поставляет файл windows.inc (в отличие от windows.h), у ассемблерного программиста в связи с этим возникает большая-пребольшая проблема. Ее можно попытаться решить несколькими путями:
Именно последний вариант и предлагается в качестве основного. Файл windows.inc, который мы вам предложили выше, взят из состава приложения MyCall. В нем только самая малая часть из того, что должно быть, и есть кое-что лишнее (RAS, например, в Visual C++ имеет собственный заголовочный файл). Но для старта этого достаточно.
Не забудьте только включить свой windows.inc в процедуру архивирования (надеемся, таковая у вас имеется). Будет обидно начинать все сначала после какой-нибудь очередной переинсталляции системы.
Ну вот, а теперь пора создать новый проект:
Новый проект создан.
В рамках вновь созданного проекта первым делом необходимо создать файл с исходным текстом:
А теперь самое важное. Файл когда-нибудь придется компилировать. Подготовиться к этому следует уже сейчас:
ml.exe /c /coff /nologo /Zi /DDEBUG $(InputPath)
.\$(InputName).obj
ml.exe /c /coff /nologo $(InputPath)
.\$(InputName).obj
Файл готов к компиляции с помощью модуля ml.exe. Обратите внимание, что пункты 5 и 6 касаются компиляции asm-файла для отладочного варианта проекта, и получаемый в этом случае объектный файл содержит отладочную информацию. Пункты же 8 и 9 касаются окончательного варианта проекта, готового к дистрибуции.
Наконец, наш файл должен иметь какое-нибудь корректное содержимое. Допустим, мы разрабатываем минимальное приложение. Учитывая, что кое-что из него уже имеется в файле @struct.inc, его содержимое может быть, например, таким:
|
include @struct.inc include c:\program files\devstudio\vc\include\windows.inc .code WinMain PROC PUBLIC hinst,prev_hinst,command_line,cmd_show invoke ExitProcess,0 WinMain ENDP end |
Обратите внимание, что имя включаемого файла windows.inc указано с полным путем. Это не случайно. В процессе разработки приложения вам придется очень часто обращаться к этому файлу, добавляя в него заголовочную информацию. В MS DevStudio это сделать очень просто, причем несколькими способами. Например, можно выделить имя файла и, щелкнув по нему правой кнопкой, выбрать пункт контекстного меню Open Document. Но удобнее все-таки подключить файл windows.inc к проекту "на постоянно", воспользовавшись правой кнопкой мыши в окне Workspace. Если вы удачно попадете по имени проекта или по какой-нибудь папке, вам станет доступна опция Add files to Project (Folder)... С ее помощью-то вы и подключите файл к проекту, и в дальнейшем простым двойным кликом сможете вызывать его на редактирование.
Теперь перейдем к сборке проекта:
Все, проект готов к сборке. Нажмите кнопку Build (F7) и убедитесь, что сборка проходит нормально. Можете также попробовать запустить полученный исполняемый модуль проекта и убедиться, что ничего не происходит: он выполняется и завершается слишком быстро и без каких-либо видимых признаков.
Выше мы обещали обеспечить удобный доступ к файлам заголовков API, чтобы можно было пользоваться ими как справочником при подготовке файла windows.inc. Для этого нам потребуется активизировать встроенное средство MS DevStudio под названием Browse Info. Работает оно очень просто. Достаточно щелкнуть по интересующему нас имени в исходном тексте программы правой клавишей мыши и выбрать пункт контекстного меню Go To Definition Of, как моментально загрузится заголовочный файл Visual C++, в котором это имя описано. В проектах на C/C++ активизация Browse Info происходит автоматически, при первой попытке выполнить указанное действие. А вот в проектах на ассемблере, к сожалению, этого нет. Но ничего страшного. Просто нужно немножко повозиться.
И наконец, маленькое замечание о зависимостях (Dependencies). Не забывайте о них. Например, файл main.asm, очевидно, зависит от windows.inc. И поскольку последний будет очень часто подвергаться изменениям, в том числе и вне текущего проекта, имеет прямой смысл эту зависимость описать. При этом в каждом случае, когда происходит изменение файла windows.inc, будет автоматически вызываться перекомпиляция файла main.asm. В MS DevStudio зависимости описываются очень просто (хотя и не очень удобно: приходится вводить имя файла вручную), гораздо проще, чем в заковыристых исторических make-файлах. Снова вызовите диалог Project Settings для файла main.asm. Найдите на вкладке Custom Build кнопку Dependencies и в появившеся диалоге укажите полное имя файла windows.inc.
Все, MS Developer Studio готово для ассемблерного программирования. Можно предъявить много всяких претензий по сервису и оформлению, но главное - это работает.
Mycall.rc для mycall
Это файл описания ресурсов для приложения MyCall. Этот файл вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файлах mycallcb.zip (C++, 13192 байта) и в mycallab.zip (ассемблер, 15913 байт). Имеется также Инструкция программиста.
Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
//ВКЛЮЧАЕМЫЙ ФАЙЛ С ОПРЕДЕЛЕНИЯМИ КОНСТАНТ
//(cтандартный файл заголовков win32 из пакета MS Developer Studio)
#include "windows.h"
//ОБЪЯВЛЕНИЕ ИМЕН ИДЕНТИФИКАТОРОВ РЕСУРСОВ
//Обычно выносится в файл заголовков
//(по умолчанию в MS Developer Studio - resource.h)
//Содержится здесь по причине крайней простоты приложения.
#define IDD_DIALOG1 101
#define IDI_ICON1 103
#define IDC_COMBO1 1000
#define IDC_COMBO2 1001
#define IDC_COMBO3 1002
#define IDC_BUTTON1 1003
#define IDC_EDIT1 1004
//ОПИСАНИЕ ДИАЛОГА
//Следует обратить внимание на наличие определения CLASS.
//Обычно его применение не рекомендуется, так как оно перегружает стандартную
//обработку диалога и передает поток диалоговых сообщений процедуре окна,
//для которого зарегистрирован указанный класс. Однако в нашем случае, когда диалог
//используется в качестве главного окна приложения, это нормально.
IDD_DIALOG1 DIALOG DISCARDABLE 100,100,217,18
STYLE WS_CAPTION|WS_SYSMENU
CAPTION "MyCall"
CLASS "MainWindowClass"
FONT 8,"MS Sans Serif"
BEGIN
COMBOBOX IDC_COMBO1,2,2,43,120,CBS_DROPDOWNLIST|WS_VSCROLL
COMBOBOX IDC_COMBO2,48,2,58,120,CBS_DROPDOWNLIST|WS_VSCROLL
OMBOBOX IDC_COMBO3,108,2,68,120,CBS_DROPDOWNLIST|WS_VSCROLL
PUSHBUTTON "Call",IDC_BUTTON1,179,2,35,13
EDITTEXT IDC_EDIT1,2,17,215,12,ES_READONLY|NOT WS_BORDER
END
//ОПИСАНИЕ ИКОНКИ
IDI_ICON1 ICON DISCARDABLE "icon1.ico"
Обсуждение статьи "Зачем он нужен, этот ассемблер?"
Читайте также:
Нам приходят письма, в которых читатели излагают свои соображения, сомнения и возражения по теме, обсуждаемой в статье Зачем он нужен, этот ассемблер? Последнее мы получили совсем недавно от MemoBreaker'а. Автор излагает в нем мнение, во многом совпадающее с мнением других людей, чьи письма мы получали ранее и обсуждали в приватных беседах по е-мэйлу. И поскольку эта точка зрения изложена в письме MemoBreaker'а наиболее полно, мы с удовольствием и благодарностью публикуем его с разрешения автора:
Hello assembler.ru,
Вот прочитал я вашу статью: "зачем он нужен, этот ассемблер?". И возникло у меня несколько соображений по этому поводу...
Сразу отмечу, что сам я начинающий ассемблерщик (на PC, а вообще АСМ я знаю еще с Z80). Я не согласен с некоторыми утверждениями этой статьи:
0) Насчет ООП - все правильно, нормально в нем разобраться мне еще не удалось, хотя я надеюсь изучить С++. Далеко не каждый из знакомых мне С++ программистов смог нормально рассказать что такое полиморфизм, инкапсуляция и т.д.
1) Включать в список файлов, которые необходимо создавать самому, windows.inc нет смысла. Так как если особо не извращаться и писать на ЧИСТОМ MASM'е, то данный и другие файлы с описанием API уже есть в стандартной поставке. Писать не очень большие проги на масме гораздо удобнее и, по-моему, быстрее, нежели использовать монструозную Visual Studio.
2) Насчет размера файлов асма и С++ все в корне не верно! Если вы что-то сравниваете, то необходимо провести несколько сравнений: во-первых минимальное приложение на асме, выводящее только MessageBox, занимает, если не ошибаюсь полтора килобайта (меньше по-моему Винда не позволяет); во-вторых минимальное приложение, имеющее функцию WinMain, занимает 4608 байт(включая иконку, без оной - 3584); в-третьих нужно сравнивать и более серьезные проекты, причем, чем больше функций у программы, тем больше выигрыш по размеру при программировании на ассемблере. В результате если написать WordPad на ассемблере и на С++, то ассемблерная прога будет в несколько раз меньше, нежели СИшная.
3) Неудобство ассемблера заключается в том, что иногда простые ошибки очень сложно отловить, так, например, было с моей (и The Byte Reaper'а тоже) программой UIN2IP. Суть в том, что в MASM32 изначально были допущены ошибки во встроенной подпрограмме (из MASM32.lib) конвертации ASCII to DWORD, есть в масме и еще пара ошибочек, но менее существенных. В отношении ошибок С++ тоже есть в чем упрекнуть, бывали случаи, когда С++ сильно ругался на куски кода, которые ошибки (по крайней мере в теории) не имели. Хуже всего, когда на код ругается линкер - понять в чем ошибка становится гораздо сложнее.
4) Есть еще несколько применений ассемблера:
- на нем удобно писать такие вещи как VxD драйвера (не знаю возможно ли это на С++)
- ассемблер используется в критичных к скорости выполнения программах (можно, конечно, и INLINE, но как сами говорили "можно и отверткой в ухе...")
Только не надо говорить, что современные процессоры позволяют добиваться приемлемых скоростей и на С++, представьте, что вы пишете движок для рендеринга, но не используя ускоритель. В данном случае С++ отстанет от асма процентов на 50 точно (если писать под конкретный процессор на ассемблере). Другой пример, более реальный, - вы пишете программу, проводящую большое количество вычислений (тот же подбор пароля), вот здесь-то скорость и пригодится как нельзя лучше!
Ну вот в принципе и все претензии. Надеюсь что они будут рассмотрены и учтены :)
--
Best regards,
MemoBreaker
P.S. Кстати, по моему личному мнению лучшее место для ассемблера в современной индустрии программо-строения - различные .DLL , в них исчезают некоторые недостатки асма и проявляются достоинства:
- почти всегда отсутствует интерфейс, следовательно над ним не придется мучиться.
- необходима высокая скорость работы, для чего асм подходит лучше всего (пример: сжатие MP3 или MPEG; пользователь как всегда хочет побыстрее!)
- также возможна оптимизация под различные MMX, 3D-NOW!, SSE и т.д.
MemoBreaker в работает с пакетом MASM32 и считает его лучшим вариантом ассемблера для PC (в письме, говоря об ассемблере, он имеет в виду именно этот пакет). В качестве основы для рабочей среды он предпочитает не Visual Studio, а:
В аргументах MemoBreaker'а есть многое, с чем можно согласиться. Например, с тем, что MASM32 - действительно идеальное средство для разработки ассемблерных приложений под Windows. И входящий в его состав файл windows.inc, который весит сейчас уже больше 800 Кбайт - действительно освобождает программиста от необходимости создавать этого монстра самому, потому что сделан достаточно хорошо, со знанием специфики и, что самое главное, постоянно обновляется. И компилятор ml.exe, который входит в пакет - всегда самой последней версии. И это при всем при том, что Microsoft не имеет к MASM32 никакого отношения (ну разве что компилятор родом оттуда). Потому что MASM32 - это детище независимых разработчиков во главе с Iсzelion'ом.
Вы всегда можете найти самую свежую версию пакета MASM32 на его официальном сайте. Кстати, дизайн сайта достаточно красноречиво говорит о дворянском происхождении MASM32, что, впрочем, ничуть не умаляет уважения к титаническому труду создателей пакета.
Но некоторые положения статьи, которые вызывают возражения у наших корреспондентов, хотелось бы пояснить.
Собираясь ставить эксперимент по сравнению ассемблерной и сишной версий MyCall, мы изначально хотели наглядно показать преимущества ассемблерной программы. У нас совсем не было планов показывать, что никаких радикальных преимуществ нет, а вот очевидные недостатки (в первую очередь - большая трудоемкость) - имеются. Мало того, мы ведь вложили в этот эксперимент несколько больше, чем вы, дорогие читатели, в прочтение нашей статьи. И поэтому вы должны представить себе наше разочарование конечными результатами.
Проведенный эксперимент совершенно не претендует на роль истины в последней инстанции. MemoBreaker предлагает провести несколько сравнений: от минимального приложения до большого проекта, и только тогда делать выводы. Наверное, такой масштабный эксперимент дал бы много свежей пищи для размышлений. К сожалению, ничего подобного мы нигде не встречали ранее, да и вряд ли встретим когда-либо. По нашим наблюдениям, то, что сделали мы с MyCall - единственная реальная попытка подтвердить (или опровергнуть?) на практике так часто выдвигаемый тезис о прекрасной приспособленности ассемблера для создания приложений для Windows.
И если абстрагироваться от оптимистических оценок, и опираться только на сухие результаты этого единственного эксперимента, то мы вынуждены повторить неутешительный вывод, уже сделанный в оригинале статьи. Нет никаких причин разрабатывать приложения для Windows на ассемблере, за исключением одной-единственной: личного предпочтения разработчика.
Другое дело, следует ли считать эту причину существенной. По нашему мнению - да, стоит. Потому что мы глубоко убеждены, что склонность к программированию на ассемблере - это не следствие образования или опыта работы, а в первую очередь следствие типа мышления данного конкретного индивидуума. И этот тезис мы повторяли и будем повторять на страницах сайта. Начиная с шуточного психологического теста и кончая этими вполне серьезными рассуждениями.
Современное программирование базируется на абстракции, причем чем дальше - тем больше. Это естественный путь, проложенный от табуляторов первых ламповых ЭВМ до той далекой точки за горизонтом, где машинные языки в конечном итоге сольются с высшим проявлением абстракции - нормальным человеческим языком.
К сожалению, дар абстракции дан людям не в равной степени. Наверное, в этом проявляется рудиментарное наследие каменного века, когда одни индивидуумы абстрактно загоняли мамонтов в ловчие ямы, а другие вполне конкретно мочили несчастных сверху булыжниками и дубинами. В результате у первых больше развилось интуитивное правое полушарие головного мозга, а у вторых - рациональное левое. И теперь, по причине отсутствия мамонтов, первые занялись программированием на C++, а вторые - на ассемблере.
Еще раз сформулируем нашу позицию касательно размера приложения, выработанную в результате эксперимента с MyCall и на основании собственного опыта разработки приложений для Windows:
Похожие аргументы мы высказываем и при рассмотрении преимуществ ассемблера в быстродействии:
В подтверждение этих тезисов приведем письмо, полученное от Dmitry S. Bobrik (спасибо ему!) буквально во время написания этой статьи:
|
Hello! Можете себе представить - компиленый Сишный код работает БЫСТРЕЕ написаного на Асме! Единственное разумное объяснение - оптимизатор MSVC пишет код учитывая особенности архитектуры процессоров Пентиум (ну и выше...). А на Асме все то ручками, да ручками... обидно даже :-/ wbr, Dmitry http://bcsoft.da.ru http://home.tula.net/frazez |
Дмитрий сообщил, что впервые об этом эффекте он прочитал в мэйл-листе sp-forth@egroups.com
И несколько замечаний в заключение:
Ошибки при вызове функций api
| Date: | 17-24 ноября 1999 | ||
| Newsgroup: | microsoft.public.masm | ||
| Subject: | How to call win32 API? (Как вызвать win32 API?) | ||
| Участвуют: | Min Xaphiosis Randall Hyde |
minwang@hotmail.com void_s@ihug.com.au rhyde@shoe-size.com |
Organization: The Internet Group Ltd |
if(dhtml){document.write(" Все сообщения: [+][-] Открывать: [несколько]");}
[+] MinРабочая среда: VC6.0/MASM6.11/NT Server 4.0 .386 .MODEL flat, stdcall PUBLIC _start .DATA .CODE _start: INVOKE MessageBox, NULL, "ok", "test", MB_OK END _start Получаю сообщение: error A2004: symbol type conflict Также пробую MessageBoxA/MessageBoxW, бесполезно. Есть идеи?
Regards, Min [+] XaphiosisHehehehhhehehhehheheh ;)
Мне бы очень понравился такой синтаксис, но вы переоцениваете возможности ассемблера... если только вы не определили макрос MessageBox, вы не можете вызывать его с текстом в кавычках! И даже если вы сделали нечто подобное, вы не включили в строки завершающий ноль.
так что следовало бы поступить так: INVOKE MessageBox, NULL, addr msg_ok, addr msg_test, MB_OK Попробуйте.. Я гарантирую, что это будет работать...
Sincerely X. [+] MinСпасибо за ответ. Но оно по-прежнему не работает :( .386 .MODEL flat, stdcall include win.inc PUBLIC _start .DATA ALIGN DWORD msg_ok BYTE "ok",0 msg_test BYTE "test",0 .CODE _start: INVOKE MessageBox, NULL, addr msg_ok, addr msg_test, MB_OK END _start Сообщение об ошибке: "error A2004: symbol type conflict"
Regards, Min [+] XaphiosisAlright... Я быстренько откомпилировал ваш код на своей системе... Напоминаю, если вы забыли: OPTION CASEMAP:NONE включает чувствительность к регистру, иначе win.inc не работает
кроме того, после MessageBox, мне кажется, вы должны вызвать ExitProcess (правда, это не влияет на компиляцию)
Наконец, я думаю, следует проверить возможность того, что ваш win.inc дефектен (довольно сомнительно)
В любом случае, вот код, который я использовал: Попробуйте и убедитесь, что он работает ;) .386 .MODEL FLAT, STDCALL OPTION CASEMAP:NONE
include
. DATA ALIGN DWORD msg_ok BYTE "ok",0 msg_test BYTE "test",0
.CODE _start: INVOKE MessageBox, NULL, addr msg_ok, addr msg_test, MB_OK call ExitProcess ;EXIT POINT END _start Sincerely, X. [+] MinThank you very much.
Оказывается, MASM611 содержит include-файл для win3.1! Поэтому я скачал пакет MASM32 - и все заработало.
Regards, Min [+] Randall HydeЕсли вам действительно нравится такой синтаксис, переходите на HLA (High Level Assembler). Он позволит вам писать код, подобный следующему: MessageBox( MB_OK, "test", "ok", NULL ); (параметры перечислены в обратном порядке, потому что HLA использует соглашения вызова Паскаля, а не C/C++. Вы можете использовать макрос для устранения этой проблемы, если она для вас существенна).
HLA будет автоматически размещать строки в памяти "только для чтения" и предоставлять вам адреса таких строк. Вы можете найти HLA на http://webster.cs.ucr.edu
Randy Hyde
Точка зрения assembler.ru изложена в статье Вызов функций API.
Параметры функции WinMain
Приложения для win32, написанные на ассемблере и собранные без runtime-библиотеки (как это сделать - см. статьи минимальное приложение и ms devstudio - среда разработки asm), работают в среде, несколько отличающейся от той, которая привычна программистам C/C++ и выше. Все дело в том, что runtime-библиотека в значительной мере занимается подготовкой этой среды. Вернее, не сама библиотека (это, конечно, не ее задача), а входящая в нее функция _WinMainCRTStartup. В частности, эта функция устанавливает значения четырех параметров функции WinMain, которую программист воспринимает как стартовую функцию приложения:
WinMain PROC PUBLIC hInstance,hPrevInstance,lpCmdLine,nCmdShow
Здесь:
И вот все эти ценные и не очень параметры программисту на ассемблере недоступны. Сборка приложения без подключения runtime-библиотеки (например libc.lib) экономит от одного до нескольких десятков килобайт. Но тогда функция _WinMainCRTStartup не существует, а функция WinMain объявляется при сборке как точка входа. И следовательно, значения ее параметров неопределенны. Как же их получить?
Далее использован синтаксис вызова системных функций для masm6.1+
Дескриптор экземпляра приложения узнать очень просто:
invoke GetModuleHandleA,NULL
mov hInstance,eax
Возвращаемое функцией GetModuleHandleA в eax значение - и есть искомый дескриптор. Будучи настоящими ассемблерщиками, мы, как видите, сохраняем его в hInstance, чтобы не пропадали даром целых четыре байта памяти. Между прочим, этот дескриптор фактически представляет собой базовый адрес памяти для размещения образа приложения (image base в терминах формата PE-файла). Для самостоятельных приложений по умолчанию он обычно равен 400000h.
Следует обратить внимание на наличие суффикса A в имени функции, говорящего о том, что мы работаем с кодировкой ANSI. Если вы предпочитаете Unicode, то суффикс должен быть W. Это, так сказать, "ручной" выбор типа кодировки приложения на этапе компиляции. Поскольку функций API, имеющих две версии, большинство, то, в принципе, может иметь смысл автоматизация этого процесса, как это сделано, например, для C++ в MS Developer Studio. Ввести некую настроечную константу и, в зависимости от нее, подключать ту или иную версию функции.
Командную строку следует получать так:
invoke GetCommandLineA
mov lpCmdLine,eax
Возвращаемое в eax значение указывает на буфер в памяти, содержащий текст командной строки с завершающим нулем. Результат, правда, несколько отличается от того, который можно было бы получить из lpCmdLine, будь у нас runtime-библиотека. Функция GetCommandLineA возвращает командную строку, включая полный путь к исполняемому модулю приложения. Путь взят в кавычки, что необходимо, поскольку он может содержать пробелы. Например, командная строка может иметь такой вид:
"D:\myapp\Little Joke.exe" c: /format
Между прочим, lpCmdLine, буде оно нам доступно, содержало бы адрес в этом же самом буфере памяти, следующий за пробелом, которым завершается путь к исполняемому модулю.
Параметр показа главного окна приложения (если он вам действительно нужен) получить несколько труднее. Для этого необходимо воспользоваться фрагментом кода:
.data?
startup_info STARTUPINFO{}
.code
...
invoke GetStartupInfoA,offset startup_info
xor eax,eax
mov ax,startup_info.wShowWindow
mov nCmdShow,eax
...
Уникальная для каждого процесса структура STARTUPINFO заполняется вызывающим процессом, например, Проводником, перед запуском нашего приложения, при этом в нее заносится в том числе и значение параметра показа окна в виде члена wShowWindow, имеющего размер слова. С помощью этого кода мы считываем структуру STARTUPINFO, извлекаем из нее wShowWindow, приводим размер к двойному слову и записываем полученное значение в nCmdShow.
Таким образом, мы восстановили все, что потеряли, когда отказались от сборки ассемблерного приложения с runtime-библиотекой. Вернее, почти все, потому что runtime-библиотека делает чуть-чуть больше, чем установка параметров функции WinMain. Смайл.
И еще одно замечание. Раз уж мы выполняем сборку приложения без runtime-библиотеки, то ничто более не предъявляет к нашей пусковой функции каких-либо требований по ее именованию или числу аргументов. Мы можем назвать ее, например, my_main_function и лишить ее аргументов насовсем (если, допустим, они нам не понадобятся, или мы будем сохранять их значения как-нибудь по-другому), либо оставить только те, которые нам необходимы. Например:
my_main_function PROC PUBLIC inst_handle,command_string
Главное - объявить эту функцию точкой входа приложения с помощью опции /entry:"my_main_function" сборщика link.exe.
Шаблон оконного приложения
Каждый сайт, хоть немного причастный к программированию для Windows, считает своим долгом поместить на своих страницах шаблон оконного приложения. Assembler.ru в этом отношении ничем не хуже остальных. Почему бы и нет? Это настоящее, живое, работающее приложение, чего ж его стыдиться? Многим из нас его хватит, чтобы сказать себе с удовлетворением: "Достаточно, я в себя поверил", и вернуться к любимому третьему видеорежиму. Остальным же, возможно, оно понадобится еще много-много раз для того, чтобы начать с него новый проект, и, потратив полмесяца на его развитие, выяснить, что нужно было начинать с чего-то другого. Итак:
.386 .Model flat,stdcall
include windows.inc ;заголовочный файл API;///////////////////////////////////////////////////////////// windows.inc GetModuleHandleA PROTO :DWORD ;прототипы функций API LoadIconA PROTO :DWORD,:DWORD LoadCursorA PROTO :DWORD,:DWORD RegisterClassExA PROTO :DWORD CreateWindowExA PROTO :DWORD,:DWORD,:DWORD,:DWORD,\ :DWORD,:DWORD,:DWORD,:DWORD,\ :DWORD,:DWORD,:DWORD,:DWORD ShowWindow PROTO :DWORD,:DWORD GetMessageA PROTO :DWORD,:DWORD,:DWORD,:DWORD TranslateMessage PROTO :DWORD DispatchMessageA PROTO :DWORD ExitProcess PROTO :DWORD PostQuitMessage PROTO :DWORD DefWindowProcA PROTO :DWORD,:DWORD,:DWORD,:DWORD
WNDCLASSEXA STRUCT 8 ;структура класса окна cbSize dd ? style dd ? lpfnWndProc dd ? cbClsExtra dd ? cbWndExtra dd ? hInstance dd ? hIcon dd ? hCursor dd ? hbrBackground dd ? lpszMenuName dd ? lpszClassName dd ? hIconSm dd ? WNDCLASSEXA ENDS
POINT STRUCT 8 x dd ? y dd ? POINT ENDS
MSG STRUCT 8 ;структура сообщения hwnd dd ? message dd ? wParam dd ? lParam dd ? time dd ?
pt POINT{} MSG ENDS
NULL =0 ;разные константы IDI_APPLICATION =32512 IDC_ARROW =32512 COLOR_WINDOWFRAME =6 SW_SHOW =5 EXIT_ERROR =0ffffffffh WM_DESTROY =2
CS_VREDRAW =0001h CS_HREDRAW =0002h
WS_OVERLAPPED =00000000h ;константы стилей окна WS_CAPTION =00C00000h WS_SYSMENU =00080000h WS_THICKFRAME =00040000h WS_MINIMIZEBOX =00020000h WS_MAXIMIZEBOX =00010000h
WS_OVERLAPPEDWINDOW= WS_OVERLAPPED OR\ WS_CAPTION OR\ WS_SYSMENU OR\ WS_THICKFRAME OR\ WS_MINIMIZEBOX OR\ WS_MAXIMIZEBOX if(dhtml){document.all["include"].style.display="block";document.all["windows"].style.display="none";}
;///////////////////////////////////////////////////////////// WinMain .const app_name db "WinApp Template",0 ;имя приложения class_name db "WinApp Template Class",0 ;имя класса окна .data? win_class WNDCLASSEXA{} win_handle dd ? loop_message MSG{} .code ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::: WinMain PROC PUBLIC hinst,prev_hinst,command_line,cmd_show ;......................................................... регистрация класса окна mov win_class.cbSize,sizeof(WNDCLASSEXA) mov win_class.style,CS_HREDRAW OR CS_VREDRAW mov win_class.lpfnWndProc,offset win_procedure mov win_class.cbClsExtra,0 mov win_class.cbWndExtra,0 invoke GetModuleHandleA,NULL mov win_class.hInstance,eax invoke LoadIconA,NULL,IDI_APPLICATION mov win_class.hIcon,eax mov win_class.hIconSm,eax invoke LoadCursorA,NULL,IDC_ARROW mov win_class.hCursor,NULL mov win_class.hbrBackground,COLOR_WINDOWFRAME mov win_class.lpszMenuName,NULL mov win_class.lpszClassName,offset class_name invoke RegisterClassExA,offset win_class .if(!eax) jmp abort .endif ;......................................................... создание окна invoke CreateWindowExA,NULL,\ offset class_name,\ offset app_name,\ WS_OVERLAPPEDWINDOW,\ 100,100,300,200,\ NULL,\ NULL,\ win_class.hInstance,\ NULL .if(!eax) jmp abort .endif mov win_handle,eax ;он еще пригодится, только не сегодня ;......................................................... вывод окна на экран invoke ShowWindow,eax,SW_SHOW ;......................................................... инициализация ;здесь можно вставить все необходимые действия для первичной инициализации ;приложения (например, загрузку и разбор файла настроек) ;......................................................... цикл опроса очереди сообщений msg_loop: invoke GetMessageA,offset loop_message,NULL,0,0 .if(!eax) invoke ExitProcess,loop_message.wParam ;успешное завершение приложения .endif invoke TranslateMessage,offset loop_message invoke DispatchMessageA,offset loop_message jmp msg_loop ;......................................................... аварийное завершение abort: invoke ExitProcess,EXIT_ERROR WinMain ENDP
;///////////////////////////////////////////////////////////// Оконная процедура win_procedure PROC window_from,message,w_param,l_param .if(message==WM_DESTROY) invoke PostQuitMessage,0 ; .elseif(message==...) ;обработка остальных необходимых сообщений ; ... ; .elseif(message==...) ; ... ; .elseif(message==...) ; ... .else ;сообщение не обработано invoke DefWindowProcA,window_from,message,w_param,l_param ret .endif xor eax,eax ;сообщение обработано ret win_procedure ENDP
;############################################################# end
Вот, собственно, и все приложение. В нем реализованы фундаментальные концепции общего прикладного програмирования для Windows:
Если вы соберетесь откомпилировать приложение, вы можете сделать это с помощью MS Developer Studio, воспользовавшись нашими рекомендациями.
Только не забудьте удалить из исходного текста либо строку include windows.inc, либо содержимое файла windows.inc, если он у вас имеется в готовом виде.
Старт и завершение приложений
Поэт утверждал, что-де "все начинается с любви". Поэтам вообще свойственно сказануть что-нибудь этакое не подумав, получить гонорар - и в ресторан, а вы потом разбирайтесь, чего он имел ввиду. Например, предположим, имеется пара гомо сапиенс противоположных полов, выращенная до репродуктивного возраста в условиях, гарантированно исключающих ознакомление с процессом размножения двуполых животных вообще и вида гомо сапиенс в частности. Вопрос: сможет ли указанная пара, руководствуясь исключительно собственными наблюдениями разницы физиологического строения своих организмов и инстинктом, реализовать репродуктивную функцию? Низшие существа, как-то: хомячки - могут, проверено. А мы, цари природы, сможем? Разум не помешает? Ой, что-то сомнительно. Похоже, в процессе эволюции мы прикупили себе одно средство выживания - разум, заплатив за него другим - способностью существовать вне информационного поля социума.
Впрочем, что касается приложений win32, то здесь поэт точно был неправ. В этом случае все начинается с того, что вызывающая программа (например, Проводник) подготавливает и вызывает функцию API CreateProcess.
Ту же задачу, в принципе, решают и устаревшие функции WinExec и LoadModule. На самом деле на сегодняшний день они представляют собой всего лишь реализации той же функции CreateProcess.
Еще одно замечание, несколько офф-топик. Поскольку вызываемый процесс совершенно независим от вызывающего, то функция CreateProcess возвращает управление вызывающему процессу обычно до того, как вызываемый процесс закончит свою инициализацию. Если вы хотите, чтобы вызывающий процесс взаимодействовал с вызываемым, следует использовать функцию WaitForInputIdle для того, чтобы подождать завершения инициализации.
Функция CreateProcess:
Функция _WinMainCRTStartup, если таковая имеется, подготавливает рабочую среду для runtime-библиотеки, а также значения параметров для функции WinMain, после чего передает управление функции WinMain.
И, наконец, функция WinMain делает все, что придумает прикладной программист.
Несколько пояснений про элементы контекста. Поскольку assembler.ru не ставит своей целью продублировать всю имеющуюся в природе документацию на Windows, то эти пояснения будут самыми общими.
В ОС Windows существует множество способов закончить работу приложения. В MSDN сказано, что это произойдет, если:
Надо сказать, что этот список не окончателен. Николай Критский (nkritsky@mail.ru) подсказал еще и такой экзотический, но вполне работоспособный вариант: функцией SetErrorMode отключить выдачу системных сообщений об ошибках, а затем искусственно создать такую ошибку (например, выполнив деление на 0). Возникшая при этом исключительная ситуация приведет к завершению работы приложения.
На самом деле перечисленное разнообразие - только кажущееся, так как некоторые из указанных вариантов - это всего лишь скрытый вызов все той же функции ExitProcess. Это касается и ret в первичном потоке, и CTRL+C для консольного приложения, и даже варианта Николая Критского. Фактически в этих вариантах управление передается в существующие по умолчанию runtime-модуль, процедуру поддержки консоли или обработчик исключительной ситуации, которые и вызовут функцию ExitProcess.
Рекомендуется завершать работу приложения вызовом функции ExitProcess. При этом гарантируется, что система будет освобождена от последствий работы приложения, а именно:
Упомянутая выше функция TerminateProcess делает все то же самое, за исключением освобождения dll-библиотек, и именно поэтому не может быть рекомендована как регулярное средство завершения работы. Ее следует использовать только в каких-то специальных или чрезвычайных случаях.
В заключение необходимо заметить, что отнюдь не все ресурсы, занятые приложением, освобождаются автоматически при завершении его работы. Поэтому хороший стиль программирования предполагает, что программист самостоятельно, не надеясь на систему, должен освобождать занятые им ресурсы по мере исчерпания надобности в них или перед завершением работы приложения.
См. также статьи минимальное приложение и параметры функции WinMain.
@Struct.inc для mycall (ассемблер)
Это файл структурных макросов для приложения MyCall на ассемблере. Этот файл в текстовом формате вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файле mycallab.zip (15913 байт). Имеется также Инструкция программиста.
Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
;Этот прием совместно с завершиющим ENDIF позволяет предотвратить
;ошибочное многократное включение файла @struct.inc в проект
IFNDEF @@struct_inc
@@struct_inc=1;
;Замена штатного написания структурных операторов MASM с лидирующей точкой
;на написание с лидирующим @. Повышает читаемость листингов:
;- зритильно выравнивает левый край записи, так как плотность символа @ сравнима
;с плотностью букв, чего не скажешь о точке
;- позволяет легче обнаруживать взглядом структурные операторы, так как символ @
;хорошо заметен в потоке текста
@if equ <.if>
@while equ <.while>
@break equ <.break>
@endif equ <.endif>
@endw equ <.endw>
@else equ <.else>
@elseif equ <.elseif>
@repeat equ <.repeat>
;Пара макросов, позволяющих сохранять в стеке произвольное количество регистров.
;Это очень удобно. Пара команд процессора pusha/popa, сохраняющая все регистры разом,
;применима далеко не всегда, так как восстанавливает все регистры, в том числе и те,
;содержимое которых следовало бы оставить без восстановления. А применять цепочки
;команд push и pop некрасиво - и так ассембленый листинг вытянут в длину.
;Предлагаемые макросы позволяют записывать сохраняемые регистры в строку.
;Причем порядок регистров в обоих макросах одинаков, например: @push eax,ecx,esi и
;@pop eax,ecx,esi. Это позволяет при наборе формировать команду @pop путем
;копирования строки команды @push с последующей заменой слова @push на @pop. Таким
;образом существенно снижается количество труднообнаружимых ошибок, вроде
;перепутывания порядка сохранения/восстановления регистров.
@push MACRO p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pa,pb,pc,pd,pe,pf
FOR param,
IFNB
push param
ENDIF
ENDM
ENDM
@pop MACRO p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pa,pb,pc,pd,pe,pf
FOR param,
IFNB
pop param
ENDIF
ENDM
ENDM
; Стандартный набор директив для прикладного программирования под win32.
;Вынесен сюда, чтобы не засорять основной листинг.
;ВНИМАНИЕ! Файл @struct.inc должен включаться первым!
.386
.Model flat,stdcall
;См. примечание к первому оператору в этом файле.
ENDIF
Варианты реализации макросов @push/@pop в версии Андрея Бордачева - в статье Макросы First и Second.
Вызов функций api
В этой статье речь идет именно о вызове системных функций API win32, а не об организации процедур в ассемблере вообще. Обсуждаемая здесь тема, следовательно, существенно уже, так как многие возможности языка при вызове функций API не применяются. Например, здесь нет необходимости обсуждать описание процедур, так как функции API написаны добрыми людьми из Misrosoft, и нам самим их писать не придется. Также не придется обсуждать вызов функций с переменным числом параметров и кое-что еще.
Вызов системных функций API win32 из программы на ассемблере подчиняется набору соглашений stdcall, ведущему свою родословную в части именования функций - от языка C, а в части передачи аргументов - от языка Pascal. С точки зрения прикладного программиста и с учетом специфики Windows и MASM эти соглашения заключаются в следующем:
На системном уровне этот набор соглашений добавляется вот еще чем. Компилятор при формировании из исходного текста объектного файла добавляет к началу имени функции символ подчеркивания, а к концу - выражение вида @n, где n - десятичное число, равное количеству байт, занятому в стеке под аргументы функции. Так формируется технологическое имя, позволяющее осуществить связывание не только по имени вызываемой функции, но и по количеству ее аргументов. Благодаря ему при сборке обнаруживаются ошибки программиста в случае, когда он задал для вызываемой функции неправильное число аргументов. Конечно, этому сервису далеко до строгого контроля типов C++, но от огромного количества трудноустранимых ошибок он все-таки оберегает.
Соблюдение перечисленных соглашений обеспечивается компилятором автоматически. Для этого необходимо включить в начало исходного файла комбинацию директив:
.386
model flat,stdcall
Директиву .386 можно заменить на более высокую в зависимости от того, на какой процессор вы рассчитываете. Директива model определяет сегментную модель приложения. Для всех приложений win32 она должна иметь именно такой вид, как показано здесь.
Прежде чем переходить к рассмотрению примеров, следует обсудить еще одно обстоятельство. Оно связано с тем, что Windows, благодаря прозорливости ее создателей, поддерживает два типа кодировки текстовых символов - архаичную восьмибитную ANSI и перспективную шестнадцатибитную Unicode. Из-за этого программистам Microsoft пришлось для всех функций API, так или иначе работающих с текстовыми символами, писать по два программных варианта. Тот, который работает с кодировкой ANSI, имеет суффикс A, например, CreateFileA. Тот, который работает с кодировкой Unicode, имеет суффикс W, например, CreateFileW.
Обычно приложение ориентируется на работу только с одной из этих двух кодировок. Выбор производится программистом на этапе компиляции путем указания значения для специальной настроечной константы. В MS Visual C++, например, это константа UNICODE. Если она определена, то вызов функции CreateFile в тексте программы при компиляции незаметно для программиста преобразуется в вызов функции CreateFileW, в противном случае - в CreateFileA.
В программах на ассемблере, конечно, можно реализовать тот же механизм. А можно, руководствуясь девизом "handmade forever", делать это вручную.
Основываясь на вышесказанном, приведем пример вызова какой-нибудь функции API. Допустим, это будет уже упоминавшаяся функция CreateFile. Вот ее описание для C++, как оно дано в Platform SDK:
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
Фрагмент программы для кодировки ANSI, открывающий для чтения уже существующий файл, может выглядеть, например, так:
|
.data file_name DB "MyFile.dat",0 .code ... push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push 0 push GENERIC_READ push offset file_name call CreateFileA ... |
;имя открываемого файла ;hTemplateFile ;dwFlagsAndAttributes ;dwCreationDistribution ;lpSecurityAttributes ;dwShareMode ;dwDesiredAccess ;lpFileName |
CreateFileA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
.data
file_name DB "MyFile.dat",0
.code
...
invoke CreateFileA,offset file_name,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
...
Оператор invoke - это совсем небольшая и довольно очевидная доработка, которая, вместе с некоторыми другими, мгновенно превратила ассемблер из машинного языка в нормальный язык программирования.
Взаимодействие экземпляров приложения (вариант)
Этот материал предоставлен Геннадием Майко. Он дополняет публикацию "Взаимодействие экземпляров приложения", в которой assembler.ru излагал суть проблемы и рассматривал некоторые способы ее решения. Вариант, предложенный Геннадием, изящен и предельно прост в реализации. Приносим ему свою благодарность. Итак, слово автору:
Хотел бы предложить еще один вариант (на мой взгляд, весьма эффективный) проверки того, что приложение, написанное под Win32, было уже запущено. Этот пример навеян очень интересной и полезной книгой Jeffrey Richter "Advanced Windows" (если я не ошибаюсь, есть ее перевод на русский язык).
Идея состоит в следующем. Для приложения или DLL можно определить некоторую секцию (сегмент) как "разделяемую" (shared). Это значит, что ее содержимое будет использоваться совместно всеми экземплярами приложения. Этим можно воспользоваться для проверки факта запуска приложения во второй и более раз.
Определим в некотором отдельном сегменте переменную Cnt и проинициализируем ее 1 (или любым другим ненулевым значением). В самом начале работы приложение проверяет значение этой переменной и, если оно равно 1, присваивает ей 0. Понятно, что эта операция должна быть атомарной (во-первых, к этой переменной могут обращаться несколько программ одновременно, во-вторых, мы можем работать на многопроцессорной машине). Это можно сделать, например, с помощью команды XCHG или LOCK CMPXCGH (в последнем случае необходимо обязательно использовать префикс LOCK и определить в программе директиву .486).
Если значение этой переменной равно 1, значит программа работает первый раз. Если значение этой переменной равно 0, была запущено вторая (или более) копия приложения.
Для того, чтобы объявить некоторую секцию разделяемой, необходимо добавить к опциям компоновщика следующее: link /section:,RWS Здесь ключевым является 'S' (SHARED) в списке атрибутов сегмента.
В приложении к этому письму Вы можете найти проект для Visual C++ 6.0 с соответствующим примером (я воспользовался Вашими рекомендациями для его создания). Текст программы с соответствующими комментариями находится в файле ms.asm. В каталоге Debug есть исполняемый файл ms.exe. Запустив его один раз и открыв Task Manager, мы можем найти Image Name ms.exe в списке Processes. При втором, третьем и т.д. запуске этой программы новых процессов ms.exe в этот список не добавляется. Если в программе заменить команду jnz на jmp и пересобрать проект, то при втором и т.д. запусках добавляются новые процессы ms.exe. Обратите внимание, что запущенная программа будет работать бесконечно, поэтому, чтобы ее окончить, необходимо принудительно закончить соответствующий процесс с помощью Task Manager'a.
Работа программы проверена на двухпроцессорной машине под Windows NT 4.0.
Этот материал предоставлен Сергеем (AKA The Byte Reaper) http://www.neptunix.com. Он дополняет публикацию "Взаимодействие экземпляров приложения", в которой assembler.ru излагал суть проблемы и рассматривал некоторые способы ее решения. Вариант, предложенный Сергеем, в отличие от нашего, решает одну вполне конкретную задачу: предотвращает повторный запуск приложения, то есть реализует вторую стратегию из трех перечисленных в статье.
| Здравствуйте. Прочитал данную статью и ее обсуждение и решил предложить еще один вариант, может быть, не такой элегантный, как оба предыдущих, но все-таки работающий, короткий и примененный мною в нашей с MemoBreaker'ом программе UIN2IP. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= secAttrib SECURITY_ATTRIBUTES <> ;... ;... ;... mov secAttrib.nLength,SIZEOF secAttrib mov secAttrib.lpSecurityDescriptor,NULL mov secAttrib.bInheritHandle,TRUE invoke CreateMutex,ADDR secAttrib,1,ADDR mutex invoke GetLastError .if eax > 0 ; Вообще-то правильнее было бы сверять еах ; c ERROR_ALREADY_EXISTS, ну да ладно... invoke ExitProcess,0 ; Если такой мьютекс уже существует - .endif ; завершаем приложение =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Не совсем, конечно, использование по прямому назначению, но чем не синхронизация? ;) Всего наилучшего, Сергей AKA The Byte Reaper |
Действительно, самая что ни на есть синхронизация. Проверено под Windows 98 и NT - действительно, второй экземпляр приложения не запускается. Под Windows 95 не проверялось, но, скорее всего, это решение работать не будет, так как параметр secAttrib в вызове функции CreateMutex в этой ОС игнорируется. Впрочем, сейчас это уже не актруально: 95-я неумолимо становятся историей.
Упомянутый в письме проект находится в файле ms.zip размером 8851 байт.
Вот исходный текст приложения (ms.asm):
; Простое приложение, которое может быть запущено только один раз. ; Однажды стартовав, работает бесконечно. Для завершения: ; NT: Откройте Task Manager. На вкладке Processes выберите ms.exe. ; Нажмите кнопку End Process. ; 95/98: Нажмите Ctrl+Alt+Del. Выберите в списке приложение Ms. ; Нажмите кнопку End Task. ; При построении проекта в командную строку link.exe следует ; включить опцию /SECTION:SHS,RWS
.386 .Model flat,stdcall
; Разделяемый сегмент SHS SEGMENT Cnt dd 1 ; Флаг: 1 - работает первый экземпляр приложения; ; 0 - запущен второй (и последующие) экземпляры. SHS ENDS
; Код .code WinMain PROC PUBLIC hinst,prev_hinst,command_line,cmd_show xor eax,eax ; eax = 0 xchg eax,Cnt ; eax Cnt. Атомарный обмен eax и Cnt or eax,eax ; проверить флаг jnz L_Cont ; переход к коду для первого экземпляра
; Код для второго (и последующих) экземпляров
ret ; Завершение второго (и последующих) экземпляров
; Код для первого экземпляра приложения
L_Cont: jmp L_Cont ; Бесконечный цикл WinMain ENDP
end
Несколько комментариев от assembler.ru:
1. Опция командной строки компоновщика link.exe /SECTION:name,[E][R][W][S][D][K][L][P][X] позволяет принудительно назначать атрибуты секциям PE-файла. В данном случае секции, образованной из сегмента SHS, устанавливаются атрибуты R (доступна для чтения), W (доступна для записи), S (разделяемая). Атрибут S означает, что все процессы, запущенные с помощью одного и того же исполняемого файла ("image" в терминах PE-файла), получат общий доступ к области памяти, содержащей переменную Cnt. Всякое изменение этой переменной одним процессом будет наблюдаться другими процессами.
Возможность объявлять секцию разделяемой, вероятнее всего, была заложена в архитектуру Windows именно с целью обеспечить взаимодействие экземпляров одного и того же приложения. Какое-либо иное применение этого механизма как-то не приходит в голову.
Ms. exe демонстрирует одну интересную особенность операционной среды. Как видим, разделяемая секция, содержащая инициализированные данные, при загрузке приложения инициализируется только в случае, когда загружается первый экземпляр приложения. При загрузке же последующих экземпляров инициализации переменных не происходит, а они получают значения, установленные к этому моменту предыдущими экземплярами приложения. Это логично, и странно было бы, если бы было по-другому, но как же все-таки производители операционных систем умудряются все предусмотреть?
2. Обсуждаемое Геннадием требование атомарности обращения программы к переменной Cnt, в целом справедливое для любых операций с разделяемой памятью, применительно к данному случаю, по нашему мнению, не является строгим.
Немного найдется пользователей, которые способны запустить два экземпляра приложения один за другим с достаточно малым интервалом времени так, чтобы они успели вступить в конфликт при обращении к переменной Cnt. Однако настоящий ассемблерщик, конечно же, обязательно поставит префикс lock.
Про префикс lock Геннадий представил следующее пояснение. Как известно, этот префикс имеет смысл в многопроцессорных системах. Он блокирует на время выполнения команды, перед которой стоит, доступ к памяти со стороны других процессоров. Так вот, команда xchg гарантированно формирует сигнал LOCK# вне зависимости от того, имеется или нет при ней префикс lock (см. "Intel Architecture Software Developer's Manual. vol.2: Instruction Set Reference"). А вот команда cmpxchg такого сигнала не формирует, поэтому в случаях, когда в многопроцессорных системах имеется опасность одновременного доступа нескольких процессоров к одним и тем же данным, применение префикса для нее обязательно.
3. Рассматриваемый способ обеспечения уникальности экземпляра приложения имеет органически присущее ему отличие от способов, основанных на средствах межпроцессного обмена. Если скопировать ms.exe в другую папку и попытаться запустить обе копии приложения, мы обнаружим, что обе они успешно запустятся, потому что это разные image'ы, и разделения секции SHS между ними не происходит.
В контексте стоящей задачи, конечно, это несущественно, так как даже в эпоху морального устаревания 8-гигабайтных бытовых винчестеров, опять же, весьма немногие пользователи увлекаются содержанием на диске нескольких копий приложений. Но все равно интересно.
4. Разделение сегмента между экземплярами приложения можно использовать не только для обеспечения уникальности экземпляров, но и для реализации сколь угодно сложного обмена информацией между ними (третья стратегия), ведь мы можем добавить в разделяемый сегмент любой набор необходимых нам для этого переменных. Это даже удобнее, чем File Mapping, так как не требует специальной поддержки программой, не связано с использованием файловой системы, и к этим переменным можно обращаться по именам, а не по смещениям.
А вот здесь уж точно без атомарности не обойтись. Точнее, без средств межпроцессного взаимодействия. Еще точнее - без средств синхронизации процессов. И уж совсем точно - без объектов mutex и/или event. Потому что, передавая друг другу данные, процессы должны обеспечить согласованный доступ к разделяемой памяти, то есть сообщать друг другу о готовности к передаче и приему данных и не допускать обращения к неготовым или изменяемым в данный момент данным.
В целом решение, предложенное Геннадием, следует признать очень интересным и совершенно функциональным. Еще раз большое ему спасибо.
Взаимодействие экземпляров приложения
Вспоминается одна смешная история. Во времена Word 6.0, работая как-то в одной солидной организации, трепетно относившейся к своим коммерческим тайнам, был приглашен возмущенным соседом по комнате к его компьютеру. На экране красовалось нечто вроде: "Вы не можете открыть этот очень конфиденциальный документ, так как его уже открыл пользователь имярек". Имяреком был назван начальник отдела эксплуатации, сидевший в другом конце здания. Параноидальное сознание сразу стало выстраивать сложную схему шпионажа через локальную сеть, который развернул этот самый начальник. Другая часть мозга, пользуясь невытесняющей многозадачностью, принялась прикидывать размер вознаграждения за его поимку. Короче, помчались разбираться. Через пять минут половина отдела эксплуатации во главе с недоумевающим начальником толклась около компьютера и выдвигала идеи. В итоге оказалось, что никакого шпионажа нет, а ...
... а во всем виноваты Word 6.0, невнимательный пользователь и пиратство. Пиратство виновато потому, что так тогда делали все. Word 6.0 инсталлировался с дискет, которые добрый начальник отдела эксплуатации по очереди раздавал всем желающим, так что под его именем рожала документы вся организация. Невнимательный пользователь виноват потому, что редактировал-редактировал документ в одном экземпляре Word'а, а потом взял, да и попытался открыть его из файл-менеджера еще раз. А Word виноват потому, что уж так он тогда обеспечивал взаимодействие нескольких экземпляров приложения (сейчас гораздо лучше).
Больше всех жалко начальника отдела эксплуатации, которому пришлось прилично понервничать. Пользователь-то, которому его заложил глупый Word, был сотрудником отдела безопасности!
Многозадачность Windows, являясь безусловным благом, не подлежащим ныне обсуждению и сомнению, является вместе с тем источником многих вызовов, бросаемых программисту. Некоторые из них весьма изощренны и ответ на них требует глубокого понимания принципов организации рабочей среды.
Например, при разработке приложения MyCall пришлось столкнуться с такой проблемой. Как известно, Remote Access Service (RAS), работающий в асинхронном режиме, запускается в отдельном потоке (нити), отличном от основного потока приложения. Также не менее хорошо известно, что для контроля процесса установления соединения в RAS используется callback-функция (например, RasDialFunc), которую работающий RAS вызывает при необходимости сообщить приложению о своем состоянии. Так вот, если у программиста имеется потребность передать из RasDialFunc какое-либо пользовательское сообщение окну приложения, то он может побояться воспользоваться для этой цели функцией SendMessage, полагая, что два потока, одновременно запускающие одну и ту же оконную процедуру в одном контексте - это пролог к катастрофе. Между тем такое действие вполне допустимо и безопасно, так как умная Windows, прежде чем передать управление оконной процедуре, проверяет, в каком потоке создавалось соответствующее окно, и при необходимости переключает потоки. При этом передающий поток замораживается на время обработки сообщения принимающим потоком.
Другие же проблемы многозадачности более тривиальны, даже рутинны. Одна из них встает всякий раз, когда программист начинает работу над новым проектом: что делать в случае попытки запуска приложения, если оно уже запущено?
В самом деле. Сама по себе Windows не ограничивает возможности пользователя запускать какое-либо приложение любое разумное количество раз. Выполнив два двойных щелчка по одной и той же иконке, пользователь может получить два экземпляра одного и того же приложения, и каждый из них будет функционировать в собственном процессе и собственном контексте так, как будто другого не существует. Правда, только до той поры, пока они не попытаются разделить между собой какой-нибудь ресурс (в широком смысле), который разделять невозможно или нельзя. Например, невозможно разделять коммуникационный порт. И нельзя разделять общий файл данных, если оба экземпляра приложения пишут в него противоречащие друг другу данные. Разнообразие возникающих в этом случае эффектов может порадовать самого взыскательного искателя развлечений.
Принятие решения о том, как поступать со вторым и последующими экземплярами приложения, целиком возлагается на программиста. Можно придумать много разных вариантов стратегии поведения приложения, в зависимости от назначения приложения. Среди них такие:
Яркий пример третьей стратегии реализован в Word. Если экземпляр приложения уже запущен, и пользователь выполняет двойной щелчок по какому-нибудь файлу .doc в Проводнике, то запускаемый при этом второй экземпляр Word передает реквизиты загружаемого файла первому экземпляру и завершается, а первый экземпляр загружает файл в новое окно документа.
С первой стратегией все просто. При запуске приложения ничего делать не надо - и тогда вы сможете наблюдать на экране столько экземпляров вашего приложения, сколько нужно, чтобы получить моральное удовлетворение. Главное - позаботиться, чтобы экземпляры приложения никогда не пытались разделять ресурсы, которые невозможно или нельзя разделять.
Вторая стратегия несколько сложнее. Очевидно, для ее реализации необходимо, чтобы второй экземпляр приложения каким-то образом узнал о том, что существует первый экземпляр, и завершился, не успев принести какого-нибудь вреда. К сожалению, никакого специального средства для этого не существует.
Поговаривают, что не всегда все было так трагично. Во времена наших предков, когда "Докторская" стоила 2.20, а Билл был еще совсем бедным со своими двумя десятками гигабаксов, заработанных на DOSе, это делалось очень просто. В win16 имел смысл параметр hPrevInstance функции WinMain. Достаточно было убедиться, что он ненулевой - и приложение знало, что оно не одиноко на этом свете. Но когда разрабатывалась win32, кто-то из главных системщиков Microsoft лопухнулся, а потом, когда все всплыло, на специальном совещании было принято решение прикрыть провинившегося и объяснить все произошедшее какими-нибудь благообразными словами, вроде: "Мы так и хотели с самого начала, а hPrevInstance сохранили для совместимости снизу вверх." Так или иначе - но hPrevInstance теперь бесполезен. А следует пользоваться средствами межпроцессного взаимодействия (Interprocess Communications), которые, к чести Microsoft, реализованы в Windows великолепно.
Способов дать знать второму экземпляру о существовании первого можно придумать много. Один из них прямо дается в MSDN в статье WinMain:
hPrevInstance
Identifies the previous instance of the application. For a Win32-based application, this parameter is always NULL. If you need to detect whether another instance already exists, create a named mutex using the CreateMutex function. If the GetLastError function returns ERROR_ALREADY_EXISTS, another instance of your application exists (it created the mutex).
Идентифицирует предыдущий экземпляр приложения. Для Win32-приложений этот параметр всегда равен NULL. Если вам необходимо определить, существует ли уже экземпляр приложения, создайте именованный объект mutex, используя функцию CreateMutex function. Если функция GetLastError вернет ERROR_ALREADY_EXISTS, другой экземпляр вашего приложения существует (так как он уже создал mutex).
Вот этой самой идеей и воспользуемся, слегка модифицировав ее:
.386 .Model flat,stdcall
include windows.inc ;заголовочный файл API;///////////////////////////////////////////////////////////// windows.inc OpenMutexA PROTO :DWORD,:DWORD,:DWORD ;прототипы функций API CreateMutexA PROTO :DWORD,:DWORD,:DWORD ExitProcess PROTO :DWORD
SYNCHRONIZE =00100000h ;константы типов доступа STANDARD_RIGHTS_REQUIRED =000f0000h MUTANT_QUERY_STATE =0001h MUTANT_ALL_ACCESS =STANDARD_RIGHTS_REQUIRED OR SYNCHRONIZE OR MUTANT_QUERY_STATE MUTEX_ALL_ACCESS =MUTANT_ALL_ACCESS
NULL =0 ;разные константы FALSE =0 TRUE =1 if(dhtml){document.all["include"].style.display="block";document.all["windows"].style.display="none";}
;///////////////////////////////////////////////////////////// WinMain EXIT_OK =0 ;коды выхода EXIT_ALREADY_EXIST =1 .const check_inst_mutex_name db "Check Instant Mutex 0001",0 ;имя mutex .data? check_inst_mutex dd ? ;дескриптор mutex .code ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::: WinMain PROC PUBLIC hinst,prev_hinst,command_line,cmd_show ;......................................................... проверка повторного запуска mov check_inst_mutex,0 invoke OpenMutexA,MUTEX_ALL_ACCESS,FALSE,offset check_inst_mutex_name .if(!eax) ;если mutex не существует - создать его и запустить приложение invoke CreateMutexA,NULL,TRUE,offset(check_inst_mutex_name) mov check_inst_mutex,eax .else ;если mutex существует - прервать работу приложения invoke ExitProcess,EXIT_ALREADY_EXIST .endif ;......................................................... тело приложения ;... ;... ;весь остальной текст приложения ;... ;......................................................... завершение работы .if(check_inst_mutex!=0) ;уничтожить объект mutex invoke ReleaseMutex,check_inst_mutex .endif invoke ExitProcess,EXIT_OK WinMain ENDP ;############################################################# end
Как видим, логика работы очень проста. Первым делом пытаемся открыть mutex с заданным именем.
Если вы - автор многих разных приложений, имейте в виду, что одинаковое имя mutex'а позволит запускаться только одному из этих приложений. Если ваше чувство юмора не столь развито, не забывайте модифицировать имя mutex'а в разных приложениях.
Если mutex открыть не удается, значит запускаемый нами экземпляр приложения уникален, и мы смело можем продолжат его работу. Только перед этим создадим mutex, чтобы не дать запускаться последующим экземплярам.
Ну и, конечно, при завершении работы приложения следует не забыть вызвать функцию ReleaseMutex.
В принципе, это не обязательно, так как если не освободить mutex с помощью этой функции, по завершении работы приложения он переводится в состояние WAIT_ABANDONED, которое в данном случае не мешает достижению стоящей перед нами задачи. Однако хороший стиль программирования предполагает явное освобождение занимаемых приложением ресурсов.
А вот Сергей AKA The Byte Reaper (http://neptunix.narod.ru) совершенно справедливо обратил наше внимане на то, что приведенный выше пример реализации второй стратегии сильно перегружен формальностями и реверансами в сторону MSDN. Полностью сохраняет ту же функциональность, но гораздо приятнее взору настоящего ассемблерщика предложенный Сергеем вот какой вариант:
;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= invoke CreateMutexA,NULL,0,offset(check_inst_mutex_name) invoke GetLastError .if(eax==ERROR_ALREADY_EXISTS) invoke ExitProcess,EXIT_ALREADY_EXIST .endif ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Идея проста предельно. Если создаваемый mutex уже присутствует в системе, то функция GetLastError вовращает код ERROR_ALREADY_EXISTS. Кстати, функция CreateMutex возвращает при этом вполне валидный дескриптор существующего mutex'а, но нас это совершенно не должно интересовать. Мы просто заканчиваем работу приложения - и все.
Третья стратегия существенно сложнее в реализации, а гамма возможных решений намного больше. Очевидно, ее основой должно быть не только взаимное оповещение экземплярами приложения о существовании друг друга, но и обмен между ними какой-то информацией. Все зависит от того, какой объем информации потребуется. Если это одно-два двойных слова, то может оказаться достаточным использовать систему сообщений. В других случаях имеет смысл, например, организовать разделяемую память посредством File Mapping.
Пример реализации третьей стратегии, использующий только систему оконных сообщений, можно найти в приложении MyCall. Там используется регистрируемое глобальное сообщение, которое приложение передает в широковещательном режиме в надежде, что уже запущенный экземпляр ответит ему тем же сообщением. Если такой ответ получен, то делается вывод, что один экземпляр приложения уже запущен, и запускаемый экземпляр завершает работу. Но перед этим он извлекает из ответного сообщения дескриптор главного окна работающего экземпляра и выводит это окно на первый план, чтобы удовлетворить потребность пользователя в доступе к приложению.
Здесь мы приведем версию, выполняющую ту же задачу, но несколько отличную в реализации. По сути, она является гибридом из описанной выше и примененной в MyCall: для определения факта повторного запуска приложения используется mutex, а для передачи дескриптора окна работающего приложения вновь запускаемому - глобальное сообщение.
Это полностью работающее приложение. В его основу положен шаблон оконного приложения. Фрагменты, отвечающие за обеспечение уникальности экземпляра приложения и за вывод окна работающего приложения на первый план, выделены бледно-желтым фоном. Содержимое файла windows.inc не приводится (то, что он должен собой представлять, показано в предыдущем примере. Недостающие элементы следует добавить самостоятельно, по аналогии, пользуясь заголовочными файлами для C/C++).
.386 .Model flat,stdcall
include windows.inc ;заголовочный файл API
;///////////////////////////////////////////////////////////// WinMain EXIT_OK =0 ;коды выхода EXIT_ERROR =1 EXIT_ALREADY_EXIST =2
QUESTION_PRIME_HWND =1 ;запрос идентификатора окна ANSWER_PRIME_HWND =2 ;ответ идентификатора окна
.data? win_class WNDCLASSEXA{} ;структура класса главного окна main_window dd ? ;дескриптор главного окна check_inst_mutex dd ? ;дескриптор mutex'а second_instance dd ? ;признак второго экземпляра check_inst_message dd ? ;идентификатор регистрируемого сообщения loop_message MSG{} ;сообщение для цикла обработки .const app_name db "Single Instance Application",0 class_name db "Single Instance Application Class",0 check_inst_mutex_name db "Check Instant Mutex 0001",0 check_inst_message_name db "Check Instant Message 0001",0 .code ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::: WinMain PROC PUBLIC hinst,prev_hinst,command_line,cmd_show ;......................................................... регистрация класса окна mov win_class.cbSize,sizeof(WNDCLASSEXA) mov win_class.style,CS_HREDRAW OR CS_VREDRAW mov win_class.lpfnWndProc,offset win_procedure mov win_class.cbClsExtra,0 mov win_class.cbWndExtra,0 invoke GetModuleHandleA,NULL mov win_class.hInstance,eax invoke LoadIconA,NULL,IDI_APPLICATION mov win_class.hIcon,eax mov win_class.hIconSm,eax invoke LoadCursorA,NULL,IDC_ARROW mov win_class.hCursor,NULL mov win_class.hbrBackground,COLOR_WINDOWFRAME mov win_class.lpszMenuName,NULL mov win_class.lpszClassName,offset class_name invoke RegisterClassExA,offset win_class .if(!eax) jmp abort .endif ;......................................................... создание окна invoke CreateWindowExA,NULL,\ offset class_name,\ offset app_name,\ WS_OVERLAPPEDWINDOW,\ 100,100,300,200,\ NULL,\ NULL,\ win_class.hInstance,\ NULL .if(!eax) jmp abort .endif mov main_window,eax ;.............................................. зарегистрировать глобальное сообщение mov check_inst_message,0 invoke RegisterWindowMessageA,offset check_inst_message_name .if(eax) mov check_inst_message,eax .endif ;......................................................... проверить повторный запуск mov check_inst_mutex,0 invoke OpenMutexA,MUTEX_ALL_ACCESS,FALSE,offset check_inst_mutex_name .if(!eax) mov second_instance,FALSE invoke CreateMutexA,NULL,TRUE,offset check_inst_mutex_name mov check_inst_mutex,eax .else mov second_instance,TRUE .if(check_inst_message) invoke SendMessageA,HWND_BROADCAST,check_inst_message,QUESTION_PRIME_HWND,NULL
.endif invoke ExitProcess,EXIT_ALREADY_EXIST .endif ;......................................................... вывод окна на экран invoke ShowWindow,main_window,SW_SHOW ;......................................................... инициализация ;здесь можно вставить все необходимые действия для первичной инициализации ;приложения (например, загрузку и разбор файла настроек) ;......................................................... цикл опроса очереди сообщений msg_loop: invoke GetMessageA,offset loop_message,NULL,0,0 .if(!eax) ;завершение работы .if(check_inst_mutex!=0) ;освободить mutex invoke ReleaseMutex,check_inst_mutex .endif invoke ExitProcess,loop_message.wParam .endif invoke TranslateMessage,offset loop_message invoke DispatchMessageA,offset loop_message jmp msg_loop abort: invoke ExitProcess,EXIT_ERROR WinMain ENDP
;///////////////////////////////////////////////////////////// Оконная процедура win_procedure PROC hwnd,message,w_param,l_param mov eax,message ;................................................... обработка регистрируемого сообщения .if(eax==check_inst_message) ;если получено зарегистрированное сообщение .if(check_inst_message) .if(second_instance) ;если это второй экземпляр .if(w_param==ANSWER_PRIME_HWND) ;если в сообщении получен дескриптор окна invoke SetForegroundWindow,l_param ;вывести это окно на первый план jmp worked .endif .else ;если это первый экземпляр .if(w_param==QUESTION_PRIME_HWND) ;если запрашивается дескриптор окна mov eax,hwnd ;послать ему дескриптор главного окна .if(eax==main_window) invoke SendMessageA,HWND_BROADCAST,check_inst_message,ANSWER_PRIME_HWND,main_window jmp worked .endif .endif .endif .endif jmp noworked ;........................................................ закрытие приложения .elseif(eax==WM_DESTROY) invoke PostQuitMessage,EXIT_OK jmp worked ;........................................................ обработка остальных сообщений ; .elseif(message==...) ; ... ; .elseif(message==...) ; ... ; .elseif(message==...) ; ... ;........................................................ сообщение не обработано .else noworked: invoke DefWindowProcA,hwnd,message,w_param,l_param ret ;........................................................ сообщение обработано .endif worked: xor eax,eax ret win_procedure ENDP ;############################################################# end
Общая идея такова:
См. также интересный, эффективный и простой вариант решения рассматриваемой задачи, предложенный Геннадием Майко.
Windows.inc для mycall (ассемблер)
Это файл windows.inc, который содержит константы и прототипы функций API, используемые в приложении MyCall. Подробнее... Этот файл в текстовом формате вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файле mycallab.zip (15913 байт). Имеется также Инструкция программиста.
Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
;КОНСТАНТЫ
FALSE=0
TRUE=1
NULL=0
SW_SHOW=5
ERROR_INVALID_HANDLE=6
WM_COMMAND=0111h
WM_MOVE=0003h
WM_DESTROY=0002h
WM_CLOSE=0010h
WM_SETTEXT=000ch
WM_USER=0400h
CB_ADDSTRING=0143h
CB_GETCURSEL=0147h
CB_SETCURSEL=014eh
CB_ERR=0ffffffffh
CB_RESETCONTENT=014bh
CBN_SELCHANGE=1
BN_CLICKED=0
SWP_NOSIZE=0001h
SWP_NOMOVE=0002h
SWP_NOZORDER=0004h
SM_CXSCREEN=0
SM_CYSCREEN=1
MB_OK=0
MB_ICONERROR=10h
IDC_ARROW=32512
COLOR_WINDOW=5
HWND_BROADCAST=0ffffh
GENERIC_READ=80000000h
GENERIC_WRITE=40000000h
OPEN_EXISTING=3
CREATE_ALWAYS=2
FILE_ATTRIBUTE_NORMAL=00000080h
INVALID_HANDLE_VALUE=0ffffffffh
GMEM_FIXED=0h
EWX_REBOOT=2
EWX_FORCE=4
DLGWINDOWEXTRA=30
;ПРОТОТИПЫ ФУНКЦИЙ API
GetModuleHandleA PROTO :DWORD
LoadIconA PROTO :DWORD,:DWORD
ExitProcess PROTO :DWORD
LoadCursorA PROTO :DWORD,:DWORD
RegisterClassExA PROTO :DWORD
MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD
CreateDialogParamA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
GetLastError PROTO
GetCurrentProcess PROTO
ShowWindow PROTO :DWORD,:DWORD
TranslateMessage PROTO :DWORD
DispatchMessageA PROTO :DWORD
GetMessageA PROTO :DWORD,:DWORD,:DWORD,:DWORD
DefWindowProcA PROTO :DWORD,:DWORD,:DWORD,:DWORD
PostQuitMessage PROTO :DWORD
DestroyWindow PROTO :DWORD
RegisterWindowMessageA PROTO :DWORD
SendMessageA PROTO :DWORD,:DWORD,:DWORD,:DWORD
PostMessageA PROTO :DWORD,:DWORD,:DWORD,:DWORD
SetForegroundWindow PROTO :DWORD
CreateFileA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
CloseHandle PROTO :DWORD
ReadFile PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
WriteFile PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
GetSystemMetrics PROTO :DWORD
SetWindowPos PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
GetWindowRect PROTO :DWORD,:DWORD
GetDlgItem PROTO :DWORD,:DWORD
GetFileSize PROTO :DWORD,:DWORD
GlobalAlloc PROTO :DWORD,:DWORD
GlobalFree PROTO :DWORD
RasHangUpA PROTO :DWORD
Sleep PROTO :DWORD
CreateThread PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
EnableWindow PROTO :DWORD,:DWORD
lstrcpy PROTO :DWORD,:DWORD
lstrcat PROTO :DWORD,:DWORD
RasDialA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
RasGetConnectStatusA PROTO :DWORD,:DWORD
ExitWindowsEx PROTO :DWORD,:DWORD
;ЭКВИВАЛЕНТЫ ТИПОВ ДАННЫХ WINDOWS
; Удобны для сохранения синтаксиса вызова функций API, соответствующего
;документации программиста для Windows. Лидирующий символ @ требуется из-за того,
;что имена некоторых типов данных Windows совпадают с ключевыми словами ассемблера
;(например, DWORD).
@LONG equ dd
@UINT equ dd
@WNDPROC equ dd
@int equ dd
@HINSTANCE equ dd
@HICON equ dd
@HCURSOR equ dd
@HBRUSH equ dd
@LPCSTR equ dd
@HWND equ dd
@WPARAM equ dd
@LPARAM equ dd
@DWORD equ dd
;СТУКТУРЫ WINDOWS
WNDCLASSEX STRUCT 8
cbSize @UINT ?
style @UINT ?
lpfnWndProc @WNDPROC ?
cbClsExtra @int ?
cbWndExtra @int ?
hInstance @HINSTANCE ?
hIcon @HICON ?
hCursor @HCURSOR ?
hbrBackground @HBRUSH ?
lpszMenuName @LPCSTR ?
lpszClassName @LPCSTR ?
hIconSm @HICON ?
WNDCLASSEX ENDS
POINT STRUCT 8
x @LONG ?
y @LONG ?
POINT ENDS
MSG STRUCT 8
hwnd @HWND ?
message @UINT ?
wParam @WPARAM ?
lParam @LPARAM ?
time @DWORD ?
pt POINT {}
MSG ENDS
RECT STRUCT 8
left @LONG ?
top @LONG ?
right @LONG ?
bottom @LONG ?
RECT ENDS
;ДАННЫЕ REMOTE ACCESS SERVICE
RAS_MaxEntryName=256
RAS_MaxPhoneNumber=128
RAS_MaxCallbackNumber equ RAS_MaxPhoneNumber
UNLEN=256
PWLEN=256
CNLEN=15
DNLEN=CNLEN
RASDIALPARAMS STRUCT 4
dwSize @DWORD ?
szEntryName db (RAS_MaxEntryName+1)dup(?)
szPhoneNumber db (RAS_MaxPhoneNumber+1)dup(?)
szCallbackNumber db (RAS_MaxCallbackNumber+1)dup(?)
szUserName db (UNLEN+1)dup(?)
szPassword db (PWLEN+1)dup(?)
szDomain db (DNLEN+1)dup(?)
RASDIALPARAMS ENDS
RASCS_PAUSED=1000h
RASCS_DONE=2000h
RASCS_OpenPort=0
RASCS_PortOpened=1
RASCS_ConnectDevice=2
RASCS_DeviceConnected=3
RASCS_AllDevicesConnected=4
RASCS_Authenticate=5
RASCS_AuthNotify=6
RASCS_AuthRetry=7
RASCS_AuthCallback=8
RASCS_AuthChangePassword=9
RASCS_AuthProject=10
RASCS_AuthLinkSpeed=11
RASCS_AuthAck=12
RASCS_ReAuthenticate=13
RASCS_Authenticated=14
RASCS_PrepareForCallback=15
RASCS_WaitForModemReset=16
RASCS_WaitForCallback=17
RASCS_Projected=18
RASCS_StartAuthentication=19
RASCS_CallbackComplete=20
RASCS_LogonNetwork=21
RASCS_SubEntryConnected=22
RASCS_SubEntryDisconnected=23
RASCS_Interactive=RASCS_PAUSED
RASCS_RetryAuthentication=RASCS_PAUSED+1
RASCS_CallbackSetByCaller=RASCS_PAUSED+2
RASCS_PasswordExpired=RASCS_PAUSED+3
RASCS_Connected=RASCS_DONE
RASCS_Disconnected=RASCS_DONE+1
RAS_MaxDeviceType=16
RAS_MaxDeviceName=128
RAS_MaxPhoneNumber=128
RAS_MaxEntryName=256
RASCONNSTATUS STRUCT 4
dwSize @DWORD ?
rasconnstate @DWORD ?
dwError @DWORD ?
szDeviceType db (RAS_MaxDeviceType+1)dup(?)
szDeviceName db (RAS_MaxDeviceName+1)dup(?)
RASCONNSTATUS ENDS
RASCONN STRUCT 4
dwSize @DWORD ?
hrasconn @DWORD ?
szEntryName db (RAS_MaxEntryName+1)dup(?)
szDeviceType db (RAS_MaxDeviceType+1)dup(?)
szDeviceName db (RAS_MaxDeviceName+1)dup(?)
RASCONN ENDS
Зачем нужен ассемблер - дополнение Геннадия Майко
Читайте также:
Если вы уже прочитали статью Зачем он нужен, этот ассемблер? и ее обсуждение с MemoBreaker'ом, то должны помнить, что речь там идет о том, имеет или нет смысл писать на ассемблере прикладные программы для Windows. И если этот вопрос (без сомнения, важный для некоторой маргинальной части программистского поголовья, к которой причисляем себя и мы), по-прежнему остается без ответа, то сопряженный с ним вопрос "А имеет ли смысл писать на ассемблере системные программы" особых сомнений ни у кого не вызвал. Все участники обсуждения время от времени делали реверансы в сторону драйверов устройств, обработчиков потоков данных и т.п., говоря, что, дескать, вот в них-то ого-го, йэх, ну и ну, ух ты, то есть ассемблеру самое и место. Никаких более веских аргументов, правда, привести не пытались, но консенсуса достигли.
С радостью сообщаем, что по-настоящему веские аргументы у нас теперь есть благодаря Геннадию Майко, его опыту разработчика и его письму, которое мы публикуем здесь с согласия автора и с большой благодарностью. Опыта Геннадия в разработке на самом нижнем уровне системы, на стыке с оборудованием, вполне достаточно, чтобы люди, способные воспринимать авторитетные мнения, сделали для себя правильные выводы.
Добрый день!
Прочитав вновь поднятую тему "о нужности" ассемблера, хотел бы добавить свои личные комментарии.
1. На мой взгляд, использование ассемблера полезно и оправдано в качестве "дополнения" к используемому (выбранному, навязанному) языку или среде програмирования. Я бы еще раз хотел подчеркнуть – дополнение, а не противопоставление.
В этом качестве ассемблер является одной из альтернатив для разрешения технических проблем, неизменно встречающихся при программировании. Попробую проиллюстрировать эти утверждения некоторыми примерами, понимая всю ущербность такого "доказательства". Тем не менее, все примеры ниже взяты из реальных коммерческих проектов, когда учитывалось множество факторов – время и надежность реализации, необходимая поддержка (support) этого решения в будущем, перенос на другие операционные системы и платформы, давление менеджеров, квалификация разработчиков и т.п.
- Микросхема MPEG-encoder'a выдавала поток данных не в такой последовательности, как было принято для данного процессора (проще говоря, порядок байт был big-endian, а не little-endian). В этом случае одна команда BSWAP рассматривалась как одна из 3-х других алтернатив вот такому, на мой взгляд, некрасивому решению, которое предлагалось на С или С++:
*ptData = ((*ptData & 0xFF) << 24) | ((*ptData & 0xFF00) << 8) | ((*ptData & 0xFF0000) >> 8) | ((*ptData & 0xFF000000) >> 24);
- Эта же микросхема генерировала длинную последовательность нулей в конце MPEG-потока данных, за сигнатурой, соответствующей окончанию этого потока. Эти нули необходимо было исключить. Использование команды SCASD также рассматривалось в качестве одной из 2-х альтернатив простейшего линейного поиска.
- В приложении и библиотеках программы для управления платой MPEG-encoder'a, написанной для Windows NT4, очень широко и успешно использовалась функция InterlockedExchangeAdd (по сути, на ней базировалось вся синхронизация взаимной работы нескольких потоков). При переносе этой программы под Windows 95 оказалось, что там этой функции нет (!). Реализация собственной функции с таким же названием и синтаксисом, написанной на ассемблере с использованием команды XADD, рассматривалась в качестве основной альтернативы полной переработки всего кода синхронизации.
Для себя я сделал вывод, что ассемблер является одним из вспомогательных инструментов разработки программ. Знание его не является обязательным, но оно желательно, так как это есть еще одна "степень свободы" при решении программистских задач.
2. Я знаю, по крайней мере, несколько микросхем, которые являются "micro-code driven". То есть внутри есть несколько очень специализированных процессоров, для которых необходимо писать программы. Здесь применение языка ассемблера весьма оправдано. Я знаю, по крайней мере, одну попытку написания и использования компилятора языка С для таких микросхем, которая (естественно) завершилась неудачей. Срок "жизни" такой микросхемы весьма недолог для того, чтобы разрабатывать, поддерживать и использовать какой-то язык высокого уровня при ее программировании.
Я понимаю, что это не имеет явного отношения к программированию на ассемблере для X86 и пример не очень соответствует обсуждаемой теме, но, по крайней мере, слово "ассемблер" в нем присутствует :)
3. Несколько комментариев к высказаным в статье областям применения ассемблера:
> - на нем удобно писать такие вещи как VxD драйвера (не знаю возможно ли это на С++)
Мне приходилось писать драйвера и на ассемблере, и на С, и на С++. Как по мне, так их "удобнее" писать на С++, но это дело вкуса и "религии".
Я могу (по памяти) привести некоторую информацию об этих проектах. Все они были написаны "с нуля" (from the scratch). Она может быть полезна при сравнении использования ассемблера и языков высокого уровня.
- Драйвер и библиотека (DLL) для специализированной платы (8 контроллеров RS-232/485, таймер, контроллер клавиатуры, несколько вспомогательных регистров), языки: asm для драйвера, С для библиотеки. Операционная система Windows 3.11/95. Система "задышала" через 6 месяцев, еще 9 месяцев система модернизировалась (были изменения в "железе" и требованиях к функциональности).
- Драйвер, библиотека и приложения для платы MPEG-encoder'a, языки: С для драйвера, С++ для библиотеки и приложения. Операционная система Windows NT4. Система "задышала" через 4 месяца, еще 2 месяца ушло на "доводку". При изменении "железа" адаптация занимала пару недель.
- Драйвер, библиотека и приложения для другой платы MPEG-encoder'a, языки: С++ для драйвера, библиотеки и приложения. Операционная система Windows NT4/2000. Система "задышала" через 3 месяца, еще 1 месяц ушел на "доводку". Драйвер изначально поддерживал работу с несколькими функционально однотипными платами и в нем использовалось и наследование, и абстрактные классы, и виртуальные функции, и статические элементы классов, т.е. практически полный набор методов ООП.
Такие большие сроки разработки не должны удивлять – большая часть времени уходила на тестирование и отладку незнакомого "железа", а так же включала все "накладные расходы" работы компании (например, в двух случаях был переезд из одного оффиса в другой).
>- ассемблер используется в критичных к скорости выполнения программах
> (можно, конечно, и INLINE, но как сами говорили "можно и отверткой в ухе...")
Я думаю, что MPEG-encoder является "критичной к скорости выполнения" программой и она действительно требует очень много времени (например, 3 платы MPEG-encoder'ов (установленных на одном компьютере), в которых мультиплексирование аудио- и видео-данных проводилось в программе, а не в микросхеме, при одновременной работе занимали более 75 % времени работы процессоров). Но у нас не возникало даже идеи переписать все это на ассемблере! Я думаю, причина этого – требуемые очень большие временнЫе ресурсы разработки. Система просто не успела бы выйти на рынок; с другой стороны, предложения типа "поставьте мощный компьютер" не вызывали у клиентов никаких возражений.
А вот где ассемблер действительно, по моему, нужен, так это в "недрах" операционной системы (например, при переключении задач), где может потребоваться использование специальных команд процессора, которых нет в языках программирования высокого уровня.
Еще раз резюмируем сказанное специально для тех, до кого авторитетные мнения доходят с трудом:
Мы на 100% согласны с этими выводами, и поэтому, как раз сейчас приступая к новому прикладному проекту, уже окончательно определились с базовым языком. Конечно же, это будет ассемблер.
Зачем он нужен, этот ассемблер?
Читайте также:
Вопрос о том, имеет ли смысл заниматься разработкой приложений для Windows на ассемблере, довольно часто возникает в разных дискуссиях, форумах и конференциях Usenet. Как правило, обсуждающие высказывают при этом точки зрения, основанные на личных впечатлениях, которые не могут убедить никого, кроме их самих. Спектр мнений лежит в интервале от "в эпоху объектно-ориентированного программирования, на котором зиждется вся Windows, это полная дурость" и до "программировать на ассемблере под Windows легче, чем на C++".
Мы не тешим себя надеждой дать на этот вопрос однозначный ответ. Более того, мы уверены, что эта статья вообще ничего не изменит во взглядах ее читателей. Те из них, кто считал ассемблер подходящим прикладным языком, так и будут считать дальше. А те, кто считал первых чудаками, только укрепятся в собственном мнении.
Потому что этот вопрос не имеет объективного, независимого от каждого конкретного человека, ответа. Ответ на него лежит в области психологии, мироощущения.
Скажите пожалуйста, почему объектно-ориентированное программирование, имея уже довольно долгую историю, так и не заняло на сегодняшний момент подобающего ему первого и единственного места? Оно продвигается в жизнь программистским авангардом, в первую очередь Microsoft, и благодаря этим усилиям явочным порядком все-таки постепенно завоевывает мир. Некоторые из его элементов, как, например, инкапсуляция, стали очевидной нормой. Но 90% программистов, честно перебрав свой повседневный инструментарий, вряд ли найдут в нем, например, перегрузку операций, наследование, абстрактные классы, несмотря на то, что этим темам посвящена большая часть любого учебника по C++. Как следствие - перспективные технологии вроде COM встречают брюзжание и неприятие со стороны широких масс. Один руководитель серьезного коллектива разработчиков ответил на вопрос, почему его люди до сих пор пишут в стиле Фортрана, примерно так: "Некогда фигней заниматься, дел полно" (при том, что на старте в этот момент находился крупный проект, который по своим характеристикам как нельзя лучше ложился на COM).
Все дело в том, что далеко не все люди смотрят на мир объектно-ориентированно. Как не все мы одинаково годимся в поэты, диспетчеры АЭС, киллеры и мотогонщики, так не всем нам дан Богом дар абстракции. Проектируя приложение и принимая принципиальные решения, или выбирая, как построить конкретный фрагмент программы, многие видят своим мысленным взором не мир людей с характерными для каждого цветом глаз, размером кулака и профессией, а все ту же классическую фоннеймановскую архитектуру ЭВМ: АЛУ, ЗУ и прочее. Возможно, такое положение вещей определилось исторически, и когда вымрет пара-тройка поколений выпускников ФПМ и на Земле не останется ни одного кейса на винтиках, детская болезнь программирования будет навсегда забыта.
Рискнем, однако, высказать предположение, что не все так радужно. Причина, почему многие (как бы не большинство) программисты с трудом принимают объектно-ориентированную парадигму кроется во-первых, в личностных характеристиках, данных человеку от рождения, и, во-вторых, в недостатках самой парадигмы.
Что касается личностных характеристик, то попробуйте воспользоваться тестом. Шутка, конечно, но, как положено, с долей шутки.
Недостатки объектно-ориентированной парадигмы, как правило, не обсуждаются. В любом учебнике много говорится о том, как она хороша, и почти ничего - о ее проблемах. А они, тем не менее, существуют.
Взять хотя бы наследование. Каждый знает, что в его основу положен тезис, что реальный мир иерархичен, и наследование призвано, эксплуатируя привычку людей к восприятию иерархий, дать им удобный инструмент организации программ. На деле же в головах новичков такой подход порождает путаницу, от которой они потом избавляются всю оставшуюся жизнь, иногда безуспешно. Потому что наследование ООП не имеет ничего общего с иерархиями реальной жизни.
Например, популярный у авторов учебников пример со студенческой группой. Вершиной иерархии в этом примере является класс Student, имеющий базовый набор свойств и методов. Нижележащие уровни иерархии составляют наследующие классы - староста группы, участник научного общества, член сборной университета по бейсболу и прочее. Эта иерархия поставлена с ног на голову относительно того, что мы наблюдаем в реальной жизни. Иерархии реальной жизни основаны не на общих наборах свойств у их членов, а наоборот, на различиях. Староста группы, несомненно, выше в реальной иерархии, чем рядовой студент. Миллионер выше представителя среднего класса, хоть у обоих и по две ноги и две руки.
Еще больше отличается наследование ООП от одноименного явления живой природы. Здесь одинаковые названия порождают просто невообразимую путаницу. Отношения детей и родителей в живой природе вообще не иерархичны в том смысле, который подразумевает наследование ООП. Дети - это не копии своих родителей, имеющие кое-какие дополнительные свойства, и кое-какие видоизмененные родительские. Слишком часто дети вообще ничем не напоминают своих родителей.
В итоге наследование, будучи объективно чрезвычайно полезным инструментом, из-за выбора популяризаторами ошибочных аналогий с трудом добирается до мозгов загнанных текучкой разработчиков.
Современное программирование имеет гигантский набор разнообразных инструментов, более-менее универсальных и специализированных, мощных и простых, технологичных и навороченных, для дилетантов и для профессионалов. И каждый из них, по большому счету, является всего лишь набором приспособлений, помогающих решению тех или иных задач. Облегчая решение одних задач, он усложняет решение других.
В массовом сознании существует лишь один по-настоящему универсальный инструмент - C/C++. Всякий программист, работая с другими языками, постоянно имеет ввиду существование этого самого профессионального из всех профессиональных инструментов. То есть как бы ощущает себя не до конца профессионалом.
То есть выбор не так уж велик. Либо ты владеешь C/C++, и ты абсолютный профессионал, либо нет - и ты как бы немножко ненастоящий программист.
А уж осознав это, и даже, может быть, став мастерами C/C++, люди вспоминают об ассемблере. А еще чаще о нем вспоминают новички, желающие стать профессионалами и только-только выбирающие свой путь. Вот почему так часто возникают вопросы: имеет ли смысл программировать на ассемблере.
Есть и еще одно обстоятельство, стимулирующее интерес к ассемблеру. Под Windows на нем действительно программировать много легче, чем под DOS. Самое главное, благодаря flat-модели ушла в прошлое возня с сегментами памяти и сегментными регистрами. А сервис API делает теперь многое из той рутины, на которую уходила раньше большая часть рабочего времени программиста.
В этой статье мы избрали вот какой способ ответа на заглавный вопрос. Возьмем какую-нибудь задачку, достаточно ограниченную, чтобы можно было ее анализировать, но в то же время достаточно серьезную, чтобы можно было наблюдать тенденции. И напишем ее два раза - на ассемблере и на C++. При этом не станем применять никаких специфических для того и для другого языка приемов, чтобы сохранить сравнимость результатов. И посмотрим, какие выводы из этого можно сделать.
Итак, приложение называется MyCall и представляет собой простейший пользовательский интерфейс для Remote Access Service, то есть звонилку. Реализовано на C++ и на ассемблере. Имеются исходные тексты:
Вариант на C++:
Вариант на ассемблере:
Общие для обоих вариантов:
Начнем сравнение с трудозатрат программиста. Непосредственно сравнить время разработки не представляется возможным, так как порядок разработки был такой: сначала была написана реализация на C++, а затем, пользуясь уже отработанными решениями - на ассемблере. Можно отметить только субъективное ощущение, что на ассемблере приложение писалось бы несколько дольше, процентов на 20.
Зато можно сравнить объективные показатели.
Объем исходного текста на C++ - 14 Кбайт, на ассемблере - 17,5 Кбайта. Во втором случае пришлось сделать почти на 20% больше ударов по клавишам. Туннельного синдрома, конечно, не заполучил, но ведь и приложеньице-то масенькое.
Число строк исходного текста на C++ - 384, на ассемблере - 693. Почти в два раза больше приходится скроллить листинг вверх-вниз.
Кроме того, для реализации на ассемблере пришлось тратить время на составление файла системных заголовков windows.inc.
В целом можно сделать вывод, что общие трудозатраты на разработку небольшого приложения в хаотическом стиле на ассемблере на 20-30% больше, чем на C/C++. Что касается больших проектов, то все зависит от того, насколько правильно будет организована работа и насколько принимаемые технические решения будут соответствовать задаче. То, что для C++ и для ассемблера эти решения будут разными - очевидно, поэтому заранее трудно сказать, какие из них ускорят разработку, а какие замедлят. Можно ведь и со всей мощью ООП утонуть в соблюдении формальных правил стиля, диктуемого языком, а можно и в изначально хаотическом языке найти такие приемы, которые дадут многократный прирост производительности труда. Тем более что в среде Windows объектно-ориентированное программирование становится вполне доступным ассемблеру (например, COM. Недавно в одной из конференций прозвучало мнение, что с COM работать на ассемблере проще, чем на C++. Спорно, но поспорить есть о чем.). Но a priori следует ожидать, что разработка проекта на C++ будет несколько менее трудоемка, чем на ассемблере.
Теперь сравним результаты разработки, то есть готовые приложения. Это гораздо интереснее. Допустим, вы shareware-программист. Тогда вы должны знать, что сегодня почти невозможно придумать что-нибудь новенькое, аналогов чему не нашлось бы на бесчисленных download-серверах. Чаще всего разработанная вами программа попадает в категорию, где уже лежит десяток-другой ее близких и дальних родственников. И еще вы должны знать, что, каким бы завлекательным и подробным ни было описание приложения, первый взгляд, который бросает пользователь, играющий роль буриданова осла(по Стругацким - барана), падает на графу "размер".
Об ассемблере ходят слухи, что он позволяет писать самые компактные из компактных программы. Так ли это на самом деле?
Размер исполняемого модуля mycall.exe в реализации на C++ - 8704 байта. На ассемблере - 8704 байта.
Разочарование! Никакого выигрыша!
Стоп. Давайте сначала вспомним, что речь идет о PE-файле. И имеет смысл заглянуть внутрь него: так ли уж все одинаково в том и другом случае, как кажется на первый взгляд.
| имя секции |
назначение секции |
размер в файле | размер в памяти | ||||
| C++(speed) | C++(size) | asm | C++(speed) | C++(size) | asm | ||
| .text | код | 1000h (4096) | 0e00h (3584) | 0e00h (3584) | 0f22h (3874) | 0c84h (3204) | 0c54h (3156) |
| .rdata | константы | нет | нет | 0400h (1024) | нет | нет | 0315h (789) |
| .data | переменные | 0400h (1024) | 0400h (1024) | 0 | 042ch (1068) | 042ch (1068) | 05c1h (1473) |
| .idata | данные импорта | 0400h (1024) | 0400h (1024) | 0400h (1024) | 03eeh (1006) | 03eeh (1006) | 03eah (1002) |
| .rsrc | ресурсы | 0800h (2048) | 0800h (2048) | 0800h (2048) | 0648h (1608) | 0648h (1608) | 0648h (1608) |
Кстати, размер исполняемого модуля в реализации C++ с оптимизацией по времени выполнения несколько больше - 9216 байт. Но при программировании приложений для Windows такую оптимизацию использовать обычно нет смысла, так как многозадачная архитектура и механизм сообщений отнимают куда больше временного ресурса, чем может дать выигрыш от нее.
Вот что видно из этой таблицы:
А равенство размеров исполняемых модулей для C++(size) и ассемблера объясняется тем, что размеры секций в PE-файле выравниваются на ближайшую большую величину, по умолчанию - 200h байт.
Итак, пора подвести итог вышесказанному:
Решать, стоит ли заниматься разработкой приложений на ассемблере, должен каждый для себя сам. Существенных объективных преимуществ в разработке приложений для Windows перед C++ ассемблер не дает.
Статьи по Assembler
a compilable project. It is
;-----------------------------------------------------------------------------
; File: ServiceDX.asm
;
; Desc: Service of DirectX
;
; Note: 1.It is not a compilable project. It is only a sample!
; 2.It is not the English. It is Russian English!
;
; Copyright (c) 2001 Serge Vetroff (http://www.assembler.ru)
;-----------------------------------------------------------------------------
;##############################################################################
include @struct.inc
include windows.inc
include directx.inc
include settings.inc
include globals.inc
;*****************************************************************************
; Name: List_DX_Modes
; Desc: Listing of DirectX screen modes
; If DirectX is not available - returns FALSE
;-----------------------------------------------------------------------------
.data?
modes_buffer dd ?
modes_number dd ?
.code
List_DX_Modes PROC
@<;Clear variables>,,
@<;Create list modes buffer>,,,,
@<;Create a DirectDraw object>,,,
@<;Enum modes>,,,,,,,,
@<;Incorrect Exit>,,,,
@<;OK, truncate modes buffer>,,,,,,,
@<;Release interface>,,,,,,,<@@:>,
ret
List_DX_Modes ENDP
;.............................................................................
; Name: enum_modes_callback
; Desc: Enumeration of display modes for DirectDraw 1.0
;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
enum_modes_callback PROC USES esi edi ebx ecx lpDDSurfaceDesc,lpContext
@<;Check if buffer of modes is overloaded>,,,
@<;Test of flags of DDSURFACEDESC>,,,,,,
@<;Test of flags of DDSURFACEDESC.ddpfPixelFormat>,,,
;Copy current mode to modes buffer
@,
@,
@,
@,
@<;Inc the buffer ptr and counter>,,
@<;Exit from procedure>,,,,
enum_modes_callback ENDP
;.............................................................................
; Name: sort_the_modes
; Desc: Sort modes in the modes_buffer
;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
sort_the_modes PROC
@<;edx-number of sorting positions>,,,,
@<;Next pass>,,,,,,,
@<;Next step in a pass>,,,
@<;Compare resolution>,,,,
@<;Compare depth>,,,
@<;Exchange positions>,,,,,,,
@<; It will be not the last pass>,
@<@@:>,,,
@,
sort_the_modes ENDP
;*****************************************************************************
; Name: Set_Current_Mode
; Desc: Set the variable "current_mode" to the specified mode
;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Set_Current_Mode PROC USES esi swidth,sheight,sbpp
@,,
@@:
@,,
@,,
@,,
@,,
@,
;The mode is not found
@,,
@,,,,,,
@<;The mode is found>,,
@,
Set_Current_Mode ENDP
;*****************************************************************************
; Name: Destroy_Modes_List
; Desc: Releases memory for list of modes
;-----------------------------------------------------------------------------
Destroy_Modes_List PROC
@<@if(modes_buffer)>,,<@endif>
ret
Destroy_Modes_List ENDP
;*****************************************************************************
; Name: Get_DX_Device_Mode_Txt
; Desc: Returns pointer to the buffer with mode in text impression ("800 x 600 x 16",0)
; If mode_num is more than number of modes returns 0.
;-----------------------------------------------------------------------------
Get_DX_Device_Mode_Txt PROC USES esi edi mode_num
@<;Get mode structure>,,,,,
@
@<;print width>,,,,,,,
@<;print height>,,,,,,
@<;print bpp>,,,,
@<;exit>,,,
Get_DX_Device_Mode_Txt ENDP
;*****************************************************************************
; Name: Get_DX_Device_Mode
; Desc: Returns pointer to the MODE_DESCRIPTION structure of specified mode
; If mode_num is more than number of modes returns 0.
;-----------------------------------------------------------------------------
Get_DX_Device_Mode PROC USES esi mode_num
@<;Test if mode_num is incorrect>,,,,
@<;Calculate position of modes_buffer>,,,
@,
Get_DX_Device_Mode ENDP
;##############################################################################
end
Как писать на masm в строчку
В этой маленькой статье речь идет совсем не о том, что TASM лучше MASMа, как могут подумать те, кто ошибся в постановке ударения при прочтении названия статьи, а о том, как записывать последовательности команд в исходном тексте в одну строку.
Настоящим настоящим ассемблерщикам рекомендуется непосредственно перед прочтением нижележащего крамольного еретического текста выполнить следующие операции:
Этот совет продиктован исключительно заботой о здоровье настоящего настоящего ассемблерщика, который, как известно, испытывает непереносимые мучения от любого комфорта и просто-тки титаническим усилием воли заставляет себя писать программы с помощью мнемокодов команд, а не одной только директивы db. Ибо звучат еще в наших ушах бессмертные слова: "Ну вы, блин, заставьте нас еще горизонтально исходник скроллить, как в вижуал васике!"
Ну вот, продолжаем для оставшихся ренегатов и предателей.
Ассемблер - величайший язык, царь языков, практически лишенный недостатков, достойный всенародной любви и поклонения почти таких же, как Заместитель Руководителя Администрации Президента Российской Федерации. Но есть у него (у ассемблера, а не, упаси боже, Заместителя) один не то чтобы недостаток, а так, мелкая мелочь.
Неприятность этой мелкой мелочи мы ощутили во всем ее необозримом масштабе, когда спустя три дня по окончании гарантийного срока сдох наш любимый Sony GDM-200, и временно перекочевал под стол вместе со своими 1280х1024, а вместо него на столе оказался старенький SyncMaster 500s, с натугой выдающий 800х600. С тех пор до 80% нашего рабочего времени уходило на переключение между окнами и, чтоб он пропал совсем, скроллинг исходного текста вверх-вниз.
Существенно сократить число сгорающих в секунду нейронов нам удалось благодаря одному-единственному совсем простенькому макросу. Вот он:
@ MACRO p0,p1,p2,p3,p4,p5,p6,p7p0
p1
p2
p3
p4
p5
p6
p7ENDM
И все! Как только этот макрос был включен в нашу постоянную макробиблиотеку, мы получили счастливую и давно желаемую возможность записывать в одну строчку несколько (а именно - от 2 до 8) команд.
Возьмем, к примеру, самую что ни на есть типовую ситуацию. Нужно скопировать фрагмент памяти в новое место. Что запишет в исходный текст кто-нибудь из покинувших нас настоящих настоящих ассемблерщиков? Ну, например, нечто вроде этого:
;Copy from one buffer to another
mov esi,one_buffer
mov edi,another_buffer
mov ecx,how_many_bytes
cld
rep movsb
А что запишем мы, ренегаты и предатели? А вот что:
;Copy from one buffer to another
@
Вот какие преимущества, по нашему скромному IMHO, мы получаем в результате этого маневра:
Недостатков у предлагаемого метода только один:
Дальнейшее совершенствование предложенного метода видится нам на пути увеличения числа параметров макроса. Предварительные оценки показывают, что добавление только одного параметра p8 может привести к увеличению числа обрабатываемых макросом строк примерно на 12,5%. Желающие могут провести соответствующие эксперименты с целью уточнения этой оценки.
Желающие полюбоваться новым видом исходника и почерпнуть кое-какие стилевые идеи, могут кликнуть сюда.
Как смотреть assembler.ru локально
Допустим, вас привлекло на assembler.ru нечто, заслуживающее сохранения на локальном диске. Вы вызываете диалог "Сохранить как" и делаете все, что положено в подобных случаях. Впоследствии, пожелав оффлайново испытать однажды уже испытанное удовольствие, вы обнаруживаете, что загруженная с диска страница радикально отличается от того, что привело вас в восхищение в онлайне.
Вы, конечно же, сразу догадываетесь, что причиной конфуза является применение на assembler.ru связанных таблиц стилей и внешних скриптов. Вы начинаете лазить по кэшу, выискивая все это хозяйство, вспоминаете, что пару раз почистили кэш с тех пор, как последний раз заходили на сайт, выходите в онлайн, качаете, пытаетесь восстановить дерево каталогов, и все это с применением русского языка...
Не надо! Берегите эмоции для своих любимых! В ожидании, пока производители броузеров, отложив судебные тяжбы, введут в свои детища нормальную функцию локального сохранения документов, на нашем сайте приготовлен для вас небольшой, не очень удобный, но все-таки кое-какой сервис:
Подкаталоги на assembler.ru используются очень редко: если на странице содержится оригинальная графика, то она лежит в подкаталоге, имеющем то же имя, что и имя файла страницы. Например, страница www.assembler.ru/13_arts/13000800.htm хранит свою графику в подкаталоге www.assembler.ru/13_arts/13000800/. То же касается некоторых html-документов, имеющих подчиненное значение.
Счастливым владельцам Netscape Navigator на все вышеописанное можно не тратить время, трафик и место на диске. Ну не знает Netscape, что некоторые пользователи подключены к интернету через dial-up, ну что тут поделаешь!
Ну а если assembler.ru понравился вам до такой степени, что вы просто не сможете жить без его полной копии на вашем локальном диске, то тут вам лучше обратиться к профессионалам. Например, приобретите оффлайновый броузер Offline Explorer - и в течение нескольких минут (а то и секунд) ваша мечта исполнится. И в заключение - tip of the day: OE сможет вам пригодиться не только для закачки нашего сайта.
Лептонный стиль программирования - реализация
Это вторая часть статьи. Постановка задачи - в первой части.
В реализации задействованы файлы:
Ниже приведены фрагменты кода, включаемые
Ниже приведены фрагменты кода, включаемые в перечисленные файлы и обеспечивающие поддержку лептонного стиля программирования, и комментарии к ним.
Файл глобальных макросов @stuct.inc:
;------------------------- эквиваленты параметров молитвы @par0 EQU esi @par1 EQU edi @par2 EQU edx @par3 EQU ecx ;------------------------- макрос молитвы @pray MACRO pray,par0,par1,par2,par3 IFNB
Пояснения:
Этот текст после компиляции будет преобразован в последовательность команд:
mov esi,offset(string_buffer)
mov edi,buffer_size
mov edx,string_id
push P_GET_STRING
call supervisor
Видно, что в отличие от описанной выше реализации но основе средств обмена сообщениями Windows, этот код оптимален: кодируются только те параметры, которые используются. Другие примеры молитв и бродкастов:
@pray B_START,hinstance
@pray P_SET_MAIN_WINDOW_TITLE,offset(main_window_name)
@pray P_STORE_STATUS,
STATUS_MAIN_WINDOW_POSITION,
offset(main_window_rectangle),
show_status
@pray B_STOP
Файл глобальных констант globals.inc:
;------------------------- молитвы P_BASE =0 ;базовый номер молитв P_HINSTANCE =P_BASE+0 P_MAIN_WINDOW =P_BASE+1 P_GET_STRING =P_BASE+2 P_GET_MAIN_WINDOW_SIZE =P_BASE+3 P_SET_BACKGROUND_COLOR =P_BASE+4 ;------------------------- бродкасты B_BASE =P_BASE+100 ;базовый номер бродкастов B_START =B_BASE+0 B_STOP =B_BASE+1 B_MAIN_WINDOW_SIZED =B_BASE+2
Пояснения:
Файл главного модуля 00_main.asm:
;------------------------- список идентификаторов модулей MODULES EQU
;------------------------- include-файлы include @struct.inc include windows.inc include globals.inc ;... .code ;... ;------------------------- супервизор supervisor PROC USES ebx ecx edx esi edi pray mov eax,pray ;........................ молитвы главного модуля @if(eax==P_HINSTANCE) invoke GetModuleHandleA,NULL @elseif(eax==P_MAIN_WINDOW) mov eax,main_window @else ;....................... опрос диспетчеров модулей mov ebx,offset(dispatchers) ;ebx - указатель в списке диспетчеров @while(dword ptr[ebx]!=0) push pray call dword ptr[ebx] ;вызов очередного диспетчера @if(pray
Пояснения:
Файл модуля XX_*.asm:
;------------------------- include-файлы include @struct.inc include windows.inc include globals.inc ;... .code ;... ;------------------------- диспетчер @ dispatcher PROC USES ebx ecx edx esi edi pray mov eax,pray ;....................... обработка бродкастов @if(eax==B_START) ;... @elseif(eax==B_STOP) ;... ;....................... обработка молитв @elseif(eax==P_GET_STRING) mov buffer_address,@par0 ;пример использования параметров молитвы mov buffer_size,@par1 mov id,@par2 ;... jmp ok ;....................... завершение диспетчера @endif nok: ;не обработано xor eax,eax ok: ;обработано ret @dispatcher ENDP
Пояснения:
В заключение несколько дополнительных замечаний:
Лептонный стиль программирования
Это первая часть статьи, она содержит постановку задачи. Реализация - во второй части.
Лептоны (от греч. leptos, что значит "легкий". Ср."лепта" - мелкая разменная монета. Напр. "внести свою лепту" - сделать дела на копейку, а потом орать, что потратился на рубль) - это такие ма-а-асенькие элементарные частицы.
Существуют две теории лептонов: правильная и неправильная.
неправильная теория лептонов
По признаку участия или неучастия в сильном взаимодействии все ма-а-асенькие элементарные частицы делятся на два вида: адроны (участвующие) и лептоны (не участвующие). Об адронах мы будем говорить, если вдруг придумаем какой-нибудь адронный стиль программирования.
Пока же не придумали, то вспомним только одно: в старинных русских дворянских родах существовала традиция называть мальчиков именем Адрон, которое должно было дать будущему воину особую жизненную силу. Возможно, таким образом еще в доядерную эпоху проявлялось интуитивное понимание истинного мироздания, суть коего - иррациональное слияние метафизических и материалистических концепций, свойственное исключительно русскому человеку.
Это богоданное свойство хорошо было наблюдать летом 2000 года в очереди к мощам Св.Пантелеймона в Храме Христа Спасителя в Москве. Бесконечной вереницей стояли русские люди по 12-15 часов в надежде приобщиться святости великомученика и целителя, и такие же русские люди бесконечно обходили стоящих, собирая деньги якобы на лекарства, надгробия и восстановление храмов, и такие же русские люди с дракой и бранью прорываются в храм без очереди.
Одним из самых известных Адронов наших дней является знаменитый кинорежиссер Андрон Михалков-Кончаловский, отпрыск древнего дворянского рода Михалковых, известных своей неуемной творческой предприимчивостью, близостью к царям и неиссякаемой мужской силой. Самый популярный фильм Михалкова-Кончаловского - "Танго и Кэш", шедший в прокате города Красноголозадовска (бывш. Бухаринград) под названием "Кто девушку платит, тот ее и танцует".
Лептонов известно науке всего двенадцать штук. Точнее шесть, а остальные шесть - это их античастицы: электрон, мюон, тау-лептон, и три нейтрино: электронное, мюонное и, как вы уже можете догадаться, тау.
Нейтрино - вообще очень интересная штучка. Есть подозрения, что на самом деле Вселенная состоит в основном-то как раз из нейтрино. На миллиард нейтрино приходится одна какая-нибудь другая ма-а-асенькая элементарная частица. И основной вклад в мировую гравитацию вносят именно они - нейтрино. И если вдруг однажды мы доживем до Большого Чпока (процесса, обратного Большому Взрыву), то именно они будут в нем виноваты, свернув своей массой Вселенную обратно в сингулярность, из которой она когда-то родилась. И тогда у Кого-то на экран выскочит синяя маска смерти с предложением нажать Ctrl+Alt+Del. Но это будет уже совсем другая история.
Данная теория лептонов названа неправильной, потому что с точки зрения программирования она совершенно бесплодна.
правильная теория лептонов
Лептоны - это элементарные частицы мировой информации. Они принципиально неуловимы никакими физическими приборами. Скорость света - это недостижимая для них минимальная скорость, а обычно они движутся гораздо быстрее. Лептонное поле хранит информацию о всех прошедших, настоящих и будущих событиях в любой точке пространства-времени, включительно от чиха простудившегося этим летом микроба до мановения пальца Любимого Руководителя Товарища Ким Чен Ира, указывающего Владимиру Владимировичу направление движения России в сторону окончательной победы идей Чучхе.
В указанном промежутке лежат также разработка ОС Windows, грядущее столкновение нашей Галактики с галактикой Андромеды, передача НТВ Газпрому за долги, хрущовские испытания 50-мегатонной бомбы на Новой Земле, день рождения в прошлую субботу, раскаяние в прошлое воскресенье и многие другие события, которые здесь следовало бы перечислить, но неохота.
Единственный прибор, способный взаимодействовать с лептонами - это человеческая душа, поскольку сама она суть лептонный сгусток. Все, что для нас проявляется как интуиция, озарения, идеи - это тривиальные результаты взаимодействия души с лептонным полем (см., например, песню группы "Любэ" Улочки Московские, где есть такие пророческие слова: "Эх, сегодня я, кажись, надерусь"). Это, так сказать, непроизвольные формы взаимодействия. Существуют также и произвольные формы, то есть взаимодействия, совершаемые человеком сознательно: молитва, медитация, камлание, гадание, транс, сеанс Кашпировского, выборы президента и пр. Как правило, целью произвольных форм взаимодействия является получение из лептонного поля интересующей информации или попытка изменить его конфигурацию желательным для себя образом.
Здесь мы прекратим рассмотрение теорий лептонов, потому что все, что нужно, уже выяснили, и перейдем к практике программирования.
Среди традиционных проблем ассемблерного программиста связывание занимает далеко не последнее место.
Поясняю. Всякий сколько-нибудь значительный проект обычно содержит более одного модуля, то бишь файла с исходным текстом, по той простой причине, что будучи запихнутым в один файл, проект становится неуправляемо громоздким. Модуль еще иногда называют единицей трансляции, имея в виду то обстоятельство, что обрабатывая его и превращая в объектный файл, компилятор ни сном, ни духом не ведает о существовании других модулей. Своевременное и логически обоснованное разбиение проекта на модули столь важно, что некоторые авторы даже склонны рассматривать модульность как отдельную парадигму программирования, наряду с хаотической, процедурной, структурной и объектно-ориентированной (Собоцинский В.В. Практический курс Турбо С++. М, "Свет",1993).
Вообще-то понятие "модуль" достаточно широкое и многозначное. Но в этой статье мы будем иметь ввиду именно модуль как единицу трансляции, то есть самостоятельный asm-файл, содержащий исходный текст, который может быть откомпилирован без ошибок.
Модули должны взаимодействовать друг с другом, иначе программа не окажется единым целым и не будет работать. Взаимодействовать они могут одним из двух способов:
Возможен еще такой способ взаимодействия, как передача управления из одного модуля в другой командой jmp или ее условными формами (je, jb, js и пр.). Но, если только программист не пытается реализовать какую-нибудь специальную идею, применение этого способа следует считать проявлением запредельного случая неправильного стиля программирования.
В Windows существует еще один способ взаимодействия модулей: когда из одних модулей посылаются сообщения окнам, созданным в других модулях. Но об этом ниже.
Стоит проблема: как позволить командам из одного модуля обращаться к данным, описанным в других модулях. Или как из одного модуля вызывать процедуры, тела которых содержатся в других модулях. Решение этой проблемы как раз и называется связыванием.
Родной для ассемблерных программ способ связывания обеспечивается применением сладкой парочки директив PUBLIC и EXTRN (она же EXTERN). Директива PUBLIC объявляет имя переменной или процедуры доступным для обращения из других модулей. Директива EXTERN сообщает компилятору, что команды данного модуля могут обращаться к имени переменной или процедуры, описанной в другом модуле.
Например:
в модуле masha.asm содержится такой фрагмент кода:
PUBLIC mashin_vozrast
.data
mashin_vozrast dd 17
а в модуле vasya.asm содержится такой фрагмент кода:
EXTRN mashin_vozrast:dword
.code
;...
cmp mashin_vozrast,18
;...
Здесь дан наиболее часто применяемый вариант директив PUBLIC и EXTRN. На самом деле их возможности несколько шире, но они не являются предметом этой статьи. Читайте документацию на соответствующий вашим предпочтениям ассемблер.
Обратите внимание, что переменная mashin_vozrast объявлена как двойное слово, что вполне естественно для программирования под win32, даже при том условии, что Маша вряд ли достигнет возраста 4294967296 лет, сколько бы здоровья мы ей ни желали. Настоятельно рекомендуется в подобных случаях (когда числовая переменная объявлена как одиночная, а не как массив) всегда использовать переменные размером в двойное слово, потому что обращение к таким переменным из 32-разрядного кода существенно более компактно, чем к переменным размером в слово или байт.
Как видим, Вася пытается оценить, насколько близкими могут быть его отношения с Машей, чтобы не загреметь по очень неприятной статье. Его, вероятно, ждет разочарование, но только не на этапе компиляции, потому что компилируется он программой-компилятором (например, ml.exe) в одиночку, без участия предмета своих вожделений.
Связыванием же занимается компоновщик, он же линкер, он же link.exe. Это его основная задача, откуда и произошло его название. Схватив полученные в результате двух актов компиляции объектные файлы masha.obj и vasya.obj, он обнаруживает, что Маша публично заявляет о своем возрасте (впрочем, 17-ти лет стесняться не приходится), а Вася внимательно прислушивается. Компоновщик удовлетворяет обоих, выделяя в исполняемом модуле sex.exe место под переменную размером в двойное слово, инициализирует ее числом 17, а в команду cmp, с помощью которой Вася интересуется Машиным возрастом, подставляет адрес этой переменной.
Связывание вызовов процедур выглядит ненамного сложнее:
модуль masha.asm:
PUBLIC get_masha
.code
get_masha PROC
;...
ret
get_masha ENDP
модуль vasya.asm:
EXTRN get_masha:near
.code
;...
call get_masha
;...
Обратите внимание, что процедура get_masha объявлена как ближняя. В win32, поскольку дело происходит в плоской (flat) модели памяти, все процедуры ближние.
Как видим, Вася добрался-таки до Маши. Вероятно, в какой-то момент переменная mashin_vozrast инкрементировалась, или любовь оказалась сильнее условностей. Но так или иначе связывание состоялось.
Сказанное выше описывает стандартный для ассемблера механизм связывания на этапе компоновки приложения. По сути своей это так называемое раннее связывание, то есть связывание, выполняемое во время разработки программы. В отличие от позднего, используемого, например, в C++ для виртуальных функций, что является основой для реализации полиморфизма - одного из столпов объектно-ориентированного программирования.
Есть ли у этого метода недостатки? Да до... в общем, вам по пояс будет.
Представьте себе, что вы коренной русак без малейшей примеси немецкой крови. Тогда, видимо, вы вряд ли являетесь поклонником такого понятия, как инкапсуляция, и глобальных переменных в ваших программах - как блох на мамонте. А каждая глобальная переменная - это одна директива PUBLIC и энное количество директив EXTRN, по одной на каждый модуль, из которого на нее ссылаются. Поменяли имя переменной - будьте любезны править все модули. А если у вас, допустим, сто глобальных переменных? Не слабо?
А даже если вы и наоборот, запредельный педант, и пипифакс рвете исключительно по перфорации, и объектная ориентация для вас важнее, чем сексуальная? Тогда, вероятно, глобальных переменных в ваших программах нет совсем, а межмодульный обмен данными вы обеспечиваете с помощью специальных интерфейсных процедур, на манер публичных функций-членов классов в C++. Ну и что, намного легче вам иметь длиннющие списки директив PUBLIC и EXTRN для процедур, чем для переменных?
Возрадуйтесь! Ваши мучения закончились. С сегодняшнего дня вы можете забыть не только синтаксис директив PUBLIC и EXTRN, но и их названия, потому что они вам больше не понадобятся. На сцену выступает лептонный стиль программирования.
Отвечая на первый попавшийся FAQ, объясним сразу, при чем же здесь все-таки лептоны и почему стиль назван так претенциозно, если не сказать помпезно.
Все началось несколько лет назад, когда мы только-только начинали программировать под Windows. Почти сразу выяснив, что одно из главных достоинств ассемблерных программ - максимальное быстродействие - уже больше не является безусловным благодаря многозадачной архитектуре операционной системы, мы сначала опечалились, а потом, подумав, решили, что это даже хорошо. Можно расслабиться, перестать высчитывать загрузку конвейера процессора и позволить себе некоторые излишества: размочить позавчерашний сухарь водичкой, постирать носовой платок, не буферизировать файлы размером меньше кластера, заглянуть на Интердаму, наконец.
Особенно большое впечатление произвел механизм сообщений Windows. Допустим, нажимаете вы, не подумав, мышкой на кнопку в диалоговом окне. Внешне все происходит просто: нажимается кнопка "Нет" в диалоге "Хотите ли вы сохранить документ, над которым вы работали последние четыре часа"; вы страшно богохульничаете, плюете в монитор и с горя отправляетесь в магазин. Вот и все, и большую часть из этой процедуры вы выполняете вообще без помощи компьютера.
А на самом деле? На самом деле все ужасно сложно и долго. Драйвер мыши обнаруживает судорожное движение вашего пальца. Потом он интересуется местоположением курсора на экране. Полученные координаты он отправляет операционной системе на предмет определения окна, над которым в данный момент находится курсор. А окон открыта целая туча: всякие там кнопочки-шмопочки, документики-шмокументики, списочки-шмисочки. И все перекрываются, и каждое норовит чего-нибудь обработать. И вот операционная система перебирает их все и находит то, которое в точке курсора лежит поверх всех остальных. И отправляет ему сообщение. Методом Pоst между прочим, то есть ставя его в очередь, а не вызывая непосредственно оконную процедуру. Сообщение отправляется родительскому окну и торчит там в очереди, пока не придет пора его обработать. Кроме того, поскольку дело происходит в диалоговом окне, кнопочка еще и перерисовывается, отображая нажатое состояние. А потом еще удаляется с экрана диалоговое окно, и перерисовывается то окно, в котором находился ваш документ, а потом удаляется и оно, и перерисовываются все лежавшие под ним другие окна, при этом тоже не особенно экономя процессорное время и память. И все это сопровождается передачей такого количества сообщений, что Смольный в ночь с 24 на 25 октября (с.с.) 1917 года по сравнению с этим гвалтом покажется вам санаторием ЦК КПСС во время послеобеденного тихого часа, когда пациенты переваривают телятину по-кабардински под белым соусом в сопровождении нежных трапецеидальных импульсов стимулирующих перистальтику кремлевских таблеток. Короче, рассмотрев эту предельно упрощенную схему действий, мы понимаем, что вместо того, чтобы просто уничтожить все ваши труды, ОС сама долго и плодотворно трудится.
И что же, среди этого разврата мы будем экономить, отказывая себе во всем, даже в полкило красной икры на завтрак?
Да никогда!
Мы построим свою программу из модулей. Каждому модулю мы поручим выполнение какой-нибудь конкретной задачи в рамках общего дела. Один пусть занимается работой с настройками: содержит информацию об активных панелях управления, составе кнопок на них и пр., и предоставляет эту информацию другим модулям по их запросам. Другой модуль пусть обеспечивает сохранение состояния приложения в период между его запусками: запоминает размер и положение окон, имена редактировавшихся документов, списки изменяемых данных и еще что-нибудь. И тоже предоставляет эту информацию другим модулям по запросам. Третий модуль пусть обслуживает строку статуса: инициализирует ее, разбивает на поля, показывает в них то или иное содержимое по запросам других модулей. Четвертый модуль пусть предоставляет текстовые строки на выбранном пользователем языке. Пятый модуль...
Впрочем, достаточно. И так уже ясно, что главным элементом архитектуры нашего приложения является обмен запросами и ответами модулей между собой. По сути, модули выступают друг для друга в качестве так любимых всеми современными разработчиками клиентов и серверов. Клиенты хотят иметь информацию или требуют выполнения каких-то действий, и поэтому запрашивают соответствующий сервис у серверов. Сервера выполняют запросы клиентов, не допуская, между прочим, обслуживания некорректных запросов. (Например, некий модуль отвечает за хранение глобальных констант. Следовательно, он должен выдавать значения констант другим модулям, но не должен предоставлять им никаких средств для изменения этих значений.)
Таким образом, общая идея довольно проста и даже тривиальна. И уж конечно, в той или иной степени она реализуется практически в каждой более-менее серьезной программе.
Отличие же лептонного стиля состоит в том, что запрашивающий модуль-клиент не обязан знать, и не может знать, и вовсе даже не интересуется, кто из существующих модулей-серверов обслуживает его запрос, и вообще, существует ли такой сервер.
В этом - квинтэссенция лептонного стиля. Клиент отправляет свой запрос в пространство, не адресуясь ни к кому конкретному, наподобие молитвы всем богам. "Эй, кто-нибудь, кто может, ответьте, пожалуйста, какова сейчас ширина главного окна приложения?" - как бы кричит в эфир модуль, создающий, например, подчиненное окно, занимающее одну восьмую часть главного окна по горизонтали. И получает в ответ неизвестно от кого: "720 пикселов, дружище! Пользуйся на здоровье!".
Теперь ответим на второй первый попавшийся FAQ. Тянет ли эта идея на понятие "стиль программирования"? По-нашему, тянет. Ведь что есть стиль программирования? Это более-менее стабильный набор приемов организации программы и оформления исходного текста, используемый программистом с целью облегчения ее разработки и понимания. С точки зрения этого определения то, о чем мы сейчас говорим - именно и есть стиль. Стабилен ли он? Да вы только попробуйте его хотя бы разок, и обратно уже не запроситесь. Идет ли в данном случае речь об организации программы? Безусловно: и с формальной точки зрения (отсутствие директив PUBLIC/EXTRN), и с фактической (организация специальной системы обмена сообщениями внутри программы) - это инструмент организации программы. Идет ли в данном случае речь об оформлении исходного текста? Конечно, да. Далее мы покажем, как с помощью макросов удобно и наглядно записываются запросы клиентов.
Первая реализация поддержки лептонного стиля программирования была прямо выполнена на штатных средствах обмена сообщениями Windows. До сих пор еще некоторые наши программы используют ее. Общая идея там была такова: при запуске приложения создавалось невидимое окно, называемое нами супервизором, дескриптор которого объявлялся глобальным для всего проекта. Оконная процедура супервизора, таким образом, становилась доступной из любой точки программы для передачи (SendMessage) или посылки (PostMessage) пользовательских сообщений, то есть относящихся к диапазону WM_USER (00400h...07fffh) и/или диапазону WM_APP (08fffh...0bfffh):
invoke SendMessageA,hWnd,Msg,wParam,lParam
(см. также статью Вызов функций API)
В каждом модуле объявлялась как PUBLIC одна-единственная процедура, названная нами диспетчером. Упрощенно система работала так:
Как параметры (wParam и lParam), так и ответ (eax) могли представлять собой непосредственные данные, если они помещались в размер двойного слова, либо являться адресами (указателями) блоков памяти (структур, строк), в которые модуль-клиент и/или модуль-сервер помещали соответствующие данные. В каждом конкретном случае это определялось смыслом запроса, то есть значением сообщения (Msg).
У этой схемы, при ее общей очевидной работоспособности, имелись несколько неприятных недостатков (мы настаиваем на этом определении, ибо всякий поживший на свете человек знает, что некоторые недостатки иногда бывают очень даже приятны):
Поэтому мы не станем рассматривать здесь вариант реализации лептонного стиля на базе системы сообщений Windows, а сразу перейдем к более прогрессивному - автономному варианту. Его отличительной особенностью является применимость не только под Windows, а в любой ассемблерной программе, в том числе под DOS, Linux и пр. Если он вам понравится, вы можете использовать приведенный здесь код один к одному. Он вполне работоспособен и достаточно функционален. Но еще правильнее воспринимать его как набор идей, которые вы свободно можете развить и дополнить так, как посчитаете нужным. Или, например, перевести на другой язык программирования.
Реализация и заключительные замечания - во второй части статьи.
Настоящий ли вы ассемблерщик?
Ваш пол: муж. жен. другое
Сколько вам лет: 000
Ваш жизненный опыт:
мальчик юноша молодой джентльмен джентльмен пожилой джентльмен настоящий джентльмен
В каком порядке вы употребляете спиртные напитки:
пиво шампанское водка
пиво шампанское водка
пиво шампанское водка -не пью совсем
Играли ли вы в детстве в домино:
а что это такое? строил башенки расстреливал солдатиков кидался с балкона в прохожих "забивал козла под душистый портвейн"
Как вы отвечаете на вопрос "На каком языке вы программируете?":
на любом а на каком надо? на ассемблере на бейсике, но сейчас изучаю ассемблер добросовестно перечисляю языки ни на каком, и учиться не собираюсь сам дурак
Есть почти типовая задача, которую надо запрограммировать. Ваши действия:
ничего не делаю вообще ищу похожую программу и использую ее ищу подходящий исходник и адаптирую его пишу собственную программу, беря идеи из подходящего исходника пишу собственную программу от начала до конца
Шеф поручил вам помочь секретарше разобраться с деловой перепиской. Ваши действия:
помогаю ей разобраться пишу заявление об уходе подбираю для нее подходящий софт для делопроизводства пишу для нее базу данных на Access'е пишу для нее специализированную СУБД на ассемблере а у меня вообще нет шефа
Любите ли вы руководить людьми:
обожаю ненавижу ненавижу людей вообще этот вопрос не по теме
Вы срываете сроки разработки:
не срываю никогда на один день в два раза еще не закончил ни одной работы
хочется сделать работу получше хочется сделать похуже заказчику работы всегда больше, чем кажется платят всегда меньше, чем хочется работаю так быстро, что влияет релятивизм
Какое чувство вы испытываете к команде xor:
никакого любовь ненависть это извращение?
Все ли ошибки устранены в ваших программах:
все до единой большая часть меньшая часть ни одна моя программа еще не работала правильно это меня не волнует
| Что лучше: | mov ax,offset addr | lea ax,addr |
| Что лучше: | push es pop dx | mov ax,es mov dx,ax | Трудно сказать |
![]() | ![]() |

The first and the only natural compound herbal medicine with cardlovascular therapeutic effects succeeded in IND of FDA In USA.
Cardiotonic Pills
Cardiotonic Pill (CP) is the primary selective drug for preventing and treating cardiovascular disease. CP Protected by Chinese government and produced exclusively by Tianjin Tianshili Pharmaceutical Group Co. Ltd. CP is a pure Chinese herb medicine combining traditional Chinese medicinal theory with modern pharmaceutical techniques. The total effective rate for treating angina pectoris is 95.3%, for improving electrocardigram is 62.16%, CP has many advantages such as rapid action, high efficiency, low dosage, convenient for taking, wide uses, few side-effects and toxicities, etc.
Pharmacology:
Toxicology:
Ingredients:
Functions:
Indications:
Administration:
Cautions:
Storage:
Number of documents of ratification:

Первое и единственное природное лекарство на основе сочетания трав, для cердечно-сосудистой терапии, соответствующее требованиям IND FDA США.
Cardiotonic Pills
Cardiotonic Pill (CP) - это уникальный препарат для предупреждения и лечения сердечно-сосудистых заболеваний. CP находится под защитой китайского правительства и производится исключительно фармацевтической компанией Тяньшили Групп. CP - это чисто китайское лекарство на основе трав, сочетающее в себе традиционную китайскую медицину с современными фармацевтическими технологиями. Общие показатели эффективности при лечении пекторальной ангины (?) - 95.3 %, для улучшения электрокардиограммы - 62.16 %. CP имеет много преимуществ: быстрое действие, высокая эффективность, низкая дозировка, удобство и широта применения, незначительные побочные эффекты и токсичность и т.д.
Фармакология:
Токсикология:
Ингредиеты:
Функции:
Показания:
Указания по применению:
Предупреждения:
Хранение:
Номер документа о ратификации:
(Перевод с английского assembler.ru. Не владея специальной медицинской терминологией, будем благодарны за замечания и исправления.)
Догадка гольдбаха (версия 2.1)
Догадка Гольдбаха была высказана прусским математиком Кристианом Гольдбахом в 1742 году в письме Леонарду Эйлеру и формулируется следующим образом: "Каждое четное число, большее двух, может быть представлено в виде суммы двух простых чисел." Попытки доказать истинность Догадки Гольдбаха или подтвердить невозможность такого доказательства до настоящего времени успеха не имели. Предыдущая попытка от assembler.ru тоже была не совсем удачной. Нынешняя кажется более убедительной. Здесь используются те же идеи, но уже нет никаких кирпичей, зато есть довольно внятная математика, устранены кое-какие проблемы и выявлены новые существенные обстоятельства.
Еще одна очень интересная попытка доказательства представлена Тимом Туманным. Всем интересующимся этой проблемой рекомендуется к изучению и обсуждению в первую очередь.
Основная идея предлагаемого доказательства заключается в том, чтобы сопоставить каждому четному числу E некоторую величину SE, заведомо меньшую, чем действительное число пар простых чисел, дающих в сумме число E. Четное число E, для которого значение SE > 1, обязательно будет иметь не менее одной пары простых чисел, дающих в сумме число E. Если условие SE > 1 выполняется для всех E, то Догадка Гольдбаха верна.
Каждое четное число E можно представить в виде суммы двух чисел с помощью E/2 уникальных пар. Необходимо исключить из этого количества все пары, в которых содержатся не простые числа. При определении, является ли число простым, будем руководствоваться правилом: число является простым, если не делится на все простые числа, меньшие или равные корню квадратному из этого числа.
Возьмем для примера четное число 78. Его можно представить в виде суммы двух чисел с помощью 39 пар: 1+77 2+76 3+75 4+74 5+73 6+72 7+71 8+70 9+69 10+68 11+67 12+66 13+65 14+64 15+63 16+62 17+61 18+60 19+59 20+58 21+57 22+56 23+55 24+54 25+53 26+52 27+51 28+50 29+49 30+48 31+47 32+46 33+45 34+44 35+43 36+42 37+41 38+40 39+39.
Исключим из числа имеющихся пар те, которые содержит числа, делимые на 2. Останется либо половина от определенного в п.2 числа пар (как, например, для E=16: 1+15 3+13 5+11 7+9), либо больше половины (как для E=18: 1+17 3+15 5+13 7+11 9+9). Воспользуемся первым случаем, потому что эта величина меньше, и, следовательно, соответствует принятому методу доказательства. Итак, после исключения пар, числа в которых делятся на 2, останется

В примере с числом 78 на этом этапе подлежат исключению 19 пар: 1+77 2+76 3+75 4+74 5+73 6+72 7+71 8+70 9+69 10+68 11+67 12+66 13+65 14+64 15+63 16+62 17+61 18+60 19+59 20+58 21+57 22+56 23+55 24+54 25+53 26+52 27+51 28+50 29+49 30+48 31+47 32+46 33+45 34+44 35+43 36+42 37+41 38+40 39+39.
Остается 20 пар: 1+77 3+75 5+73 7+71 9+69 11+67 13+65 15+63 17+61 19+59 21+57 23+55 25+53 27+51 29+49 31+47 33+45 35+43 37+41 39+39.
Предлагаемая оценка дает значение 19.5, что меньше фактического числа оставшихся пар и согласуется с принятой методикой доказательства.
Исключим из числа оставшихся после п.3 пар те, которые содержат числа, делимые на 3. Следует оценить, какое наименьшее количество пар останется. Поскольку число 2, использовавшееся в предыдущем пункте, и число 3 - простые, то можно было бы утверждать, что при достаточно больших E это количество будет приближаться к 1/3 части от числа пар, оставшихся после предыдущего пункта. Однако количество пар должно выражаться натуральным числом. Следовательно, имеют место ошибки округления. Чтобы учесть их в наихудшем варианте, вычтем из результата оценки единицу:

В примере с числом 78 на этом этапе подлежат исключению 7 пар: 1+77 3+75 5+73 7+71 9+69 11+67 13+65 15+63 17+61 19+59 21+57 23+55 25+53 27+51 29+49 31+47 33+45 35+43 37+41 39+39.
Остается 13 пар: 1+77 5+73 7+71 11+67 13+65 17+61 19+59 23+55 25+53 29+49 31+47 35+43 37+41.
Предлагаемая оценка дает значение 5.5, что меньше фактического числа оставшихся пар и согласуется с принятой методикой доказательства.
Продолжая рассуждения, исключим из оставшихся после п.4 пар те, которые содержат числа, делимые на 5. Первое число в каждой паре делится на 5 примерно в 1/5 случаев. Второе число также делится на 5 тоже примерно в 1/5 случаев, не обязательно других и не обязательно тех же самых. В худшем варианте те и другие - разные случаи, и тогда пар, в которых ни одно из чисел не делится на 5, примерно 3/5 от числа, оставшегося после предыдущего пункта. С учетом ошибки округления их количество:

В примере с числом 78 на этом этапе подлежат исключению 4 пары: 1+77 5+73 7+71 11+67 13+65 17+61 19+59 23+55 25+53 29+49 31+47 35+43 37+41.
Остается 9 пар: 1+77 5+73 7+71 11+67 17+61 19+59 29+49 31+47 37+41.
Предлагаемая оценка дает значение 2.3, что меньше фактического числа оставшихся пар и согласуется с принятой методикой доказательства.
(Следует обратить внимание на наличие в числе остающихся пары 5+73. Она не подлежит исключению на этом этапе, так как число 5 - простое.)
На следующем шаге исключаются пары, которые содержат числа, делимые на 7. После этого в наихудшем случае остается количество пар, описываемое выражением:

В примере с числом 78 на этом этапе подлежат исключению 2 пары: 1+77 5+73 7+71 11+67 17+61 19+59 29+49 31+47 37+4.
Остается 7 пар: 5+73 7+71 11+67 17+61 19+59 31+47 37+41.
Предлагаемая оценка дает значение ~0.643, что меньше фактического числа оставшихся пар. Вместе с тем это число меньше 1, то есть наличие пар простых слагаемых для числа 78 нашим доказательством не гарантируется (оно подтверждено эмпирически). Однако это еще не значит, что доказательство неверно.
Все числа в полученных парах - простые, так как использованное на этом шаге число 7 является наибольшим простым числом, меньшим, чем квадратный корень из 78. Дальнейшие итерации для числа 78 выполнять нет необходимости.
Обобщая сказанное выше, каждому E можно сопоставить значение, величина которого никогда не превысит фактическое количество пар простых чисел:

где PE - наибольшее простое число, удовлетворяющее условию

Работу этого выражения можно проиллюстрировать предлагаемой программой. Программа работает в броузерах, поддерживающий JavaScript и фреймы. Она моделирует процесс отсева пар, содержащих не простые числа. При этом вычисляется оценка количества остающихся пар на каждом шаге отсева (step SE[i]) и накопленная оценка (partial SE(i)). Пробуя различные E, можно убедиться, что ни та, ни другая оценка не превышают реального количества остающихся пар.
Очевидно, значения SE образуют последовательность на множестве простых чисел. В контексте доказательства достаточно выполнить ее анализ, пользуясь расчетом. Видно, что значения SE, начиная с некоторого момента, устойчиво превышают 1 и возрастают. В точках, где вступает в силу новое PE, наблюдаются отрицательные девиации значений последовательности SE. Эти девиации обусловлены тем, что в этих точках в выражении для SE появляется новый множитель (PE-2)/PE и вычитается еще одна единица. Но их влияние с ростом E, очевидно, постоянно уменьшается, поскольку E возрастает линейно, а их появление обусловлено корнем квадратным из E, и, кроме того, (PE-2)/PE стремится к 1. Следовательно, начиная с некоторого момента, девиации никогда не станут настолько велики, чтобы значение SE стало меньше 1.
Вывод. Каждому четному числу E, начиная с некоторого, можно поставить в соответствие значение SE такое, что оно заведомо меньше фактического числа пар простых чисел, дающих в сумме число E, и одновременно больше 1. Следовательно, для каждого такого четного числа существует не менее одной пары простых чисел, дающих в сумме это число. Наличие таких пар для чисел E, меньших указанного, подтверждается эмпирически. Догадка Гольдбаха доказана.
Предлагаемое доказательство, к сожалению, нельзя считать окончательным. Несмотря на то, что оценка SE кажется надежно меньшей, чем реальное количество пар простых слагаемых, существует обстоятельство, снижающее уровень доверия к ней. Это обстоятельство заключено в следующем. Используемая в доказательстве методика основана на предположении, что пары, подлежащие исключению из набора на очередном шаге, распределены примерно равномерно. Однако, это не так. Неравномерность распределения исключаемых пар на каждом шаге обусловлена совместным воздействием процессов исключения на предыдущих шагах. В результате существует вероятность, что на каком-либо из шагов окажется, что в оставшемся наборе имеется больше пар, подлежащих исключению, чем дает оценка для этого шага. И такой эффект действительно, редко, но наблюдается. Так, для четных чисел до 1000 это имеет место примерно в одном случае из ста (388 на шаге 13, 520 на шаге 11 и др.). В этих случаях эффект не влияет на правильность окончательной оценки, однако следует предположить, что на бесконечной числовой оси возможны ситуации, когда пострадает и окончательная оценка. Следует показать, что либо такие ситуации невозможны в принципе, либо внести в выражение, определяющее оценку, коррективы, позволяющие их учесть. Может быть, это просто. А может быть, такие коррективы будут сами по себе являться завуалированной формой Догадки Гольдбаха, или приведут к падению оценки меньше 1, или просто не существуют. Ответ, скорее всего, следует искать на пути анализа взаимодействий большого количества периодических процессов.
Догадка Гольдбаха (версия Тима Туманного)
Замечания:

Теорема1. Для любого целого числа t всегда существуют два равноудаленных от него простых числа.
То есть всегда t=p-c, t=m+c, где p и m - простые числа, c - некоторое целое число 0
Если верна Теорема1, то тогда для любого четного числа 2t=p+m, что равносильно утверждению Гольдбаха о том, что всякое четное число всегда можно выразить в виде суммы двух простых чисел.
Начнем с общеизвестного доказанного факта: всякое нечетное число можно выразить в виде разности квадратов двух целых чисел.
Действительно, t2-(t-1)2=2t+1 - общая формула для нечетных чисел.
5=32-22
7=42-32
и т. д.
Легко заметить, что такое разложение не всегда единственно:
15=82-72=42-12
21=112-102=52-22
35=182-172=62-12
То есть видим, что всякое составное нечетное число, имеющее простой множитель p, можно выразить в виде разности квадратов:
a2-(a-p)2=a2-a2+2ap-p2=p(2a-p)
Действительно, число 2a-p - число всегда нечетное, то есть число p(2a-p) и есть некоторое составное нечетное число, имеющее множитель p. При a=p мы получаем квадрат числа p. Ясно, что если число p входит в разложение составного нечетного числа со степенью 1, произведение остальных простых сомножителей всегда имеет вид 2a-p, где a и p - взаимно простые числа. Также ясно, что a-(a-p) не равно 1.
Итак, произведение двух простых чисел p и m есть число нечетное и составное, значит оно может быть выражено в виде разности квадратов двух целых чисел
p·m=t2-c2
По формуле разности квадратов имеем p·m=(t+c)(t-c).
Если положить t=a, c=a-p то как было показано выше t-c не равно 1.
Но по основной теореме арифметики представление числа в виде простых сомножителей единственно, следовательно, если положить p>m, тогда обязательно p=t+c и m=t-c, что равносильно утверждению Теоремы 1.
Итак, мы получили, что действительно для любого целого числа t всегда найдутся два равноудаленных от него простых числа p и m. Следовательно, Теорема Гольдбаха верна.
Дополнение.
Хорошо иллюстрирует вышесказанное метод конечных разностей. Если взять ряд квадратов натуральных чисел и образовать из него ряд, состоящий из разностей последующего квадрата с предыдущим, получим ряд нечетных чисел:
0 1 4 9 16 25 36 49 64 81 100 1 3 5 7 9 11 13 15 17 19
Легко заметить, что разность квадратов двух чисел разной четности, из которых меньшее отличается от большего более чем на 1, может быть выражена суммой некоторого нечетного количества слагаемых из ряда первых разностей. Например,
42-12=3+5+7
82-52=11+13+15 и т. д.
Ряд первых разностей представляет собой арифметическую прогрессию с разностью d=2. Сумма n членов арифметической прогрессии выражается как sn=[n(a1+an)]/2, где в данном случае n-ый член арифметической прогрессии имеет вид an=a1+(n-1)d=a1+(n-1)·2.
Итак, имеем для суммы n членов арифметической прогрессии sn=n·(a1+n-1).
Рассмотрим s3, начиная с a1=3 и далее a1=5.......
s3=3·(3+2)=15=3·5
s3=3·(5+2)=21=3·7
s3=3·(7+2)=27=3·9
То есть получаем ряд составных нечетных чисел кратных 3. То же верно и для s5:
s5=5·(3+4)=35=5·7
s5=5·(5+4)=45=5·9
составные нечетные кратные 5.
Итак, по индукции, составные нечетные числа могут быть выражены разностью квадратов двух чисел разной четности, из которых меньшее отличается от большего более чем на 1.
Итак, мы получили, что действительно для любого целого числа t всегда найдутся два равноудаленных от него простых числа p и m. Следовательно, Теорема Гольдбаха верна.
Впрочем, у серьезного и въедливого читателя может возникнуть вопрос: а не доказали ли мы теорему, обратную данной, а именно звучащую так: каковы бы ни были простые числа p и m, всегда найдется целое число t равноудаленное от них. Это доказывается тривиально. В самом деле, числа p и m - нечетные, значит числа t=(p+m)/2 и с=(p-m)/2 числа целые, откуда автоматически следует p=t+c и m=t-c. Но верно ли обратное,т. е. выполняется ли это для любого t? Обратимся снова к методу конечных разностей. Мы рассматриваем разности квадратов t2 - c2, где числа t и c разной четности. Мы уже видели, что такие разности выражаются суммой некоторого нечетного количества первых разностей и sn=n·(a1+n-1). Ясно, что всегда можно подобрать n равным некоторому простому числу m. С другой стороны, видно, что a1=(c+1)2-c2=2c+1. Тогда имеем sm=m·(2c+1+m-1)=m(2c+m). Из постулата Бертрана имеем, что на промежутке от t/2 до 2t имеется хотя бы два простых числа, одно меньшее, а другое большее t.
Пусть t-c=m простое число. Но с другой стороны t/2+b=m и t/2=c+b, откуда t/2
Итак, имеем, что какое бы целое число t мы не взяли, всегда найдется такое c, что t2-c2 равно произведению двух простых чисел p и m. Далее все ясно.
Я уже знаю, что для доказательства моей последней теоремы можно использовать понятие о квадратичных вычетах и так называемый закон квадратичной взаимности Гаусса. Но можно обойтись и без этих штук. Достаточно того, что как мы уже выяснили, произведение двух простых чисел всегда можно представить в виде разности квадратов. Но с другой стороны имеет место формула:
a2-b2=(a+b)(a-b)=(a-k)2-(b-k)2+2k(a-b)
Тогда (a-b)(a+b-2k)=(a-k)2-(b-k)2.
Пусть даны два простых числа p и m, тогда их произведение представимо в виде разности квадратов a2-b2=(a+b)(a-b), где m=(a-b) и p=(a+b). С другой стороны всегда существует некоторое простое число p1 равное p-2k. То есть имея два простых числа, равноудаленных от числа a, всегда можем получить два простых числа равноудаленных от другого числа a-k.
Например:
(8+5)(8-5)=3·13=72-42+2·3
3·(13-2)= 72-42
3·11=72-42
То есть зная доподлинно исходя из постулата Бертрана, что справа и слева от числа 7 имеются простые числа p<2·7 и m<7 и выбрав p=13 и m=3, получили простые числа 3 и 11 равноудаленные от числа 7.
Еще одно дополнение.
Утверждение Теоремы 1 равносильно следующему: всегда существует некое простое число p, которое при делении на целое число t>p/2 имеет ненулевой остаток с, который равен остатку с от деления числа t на простое число m. Например, остаток от деления 13 на 10 равен 3. С другой стороны остаток от деления 10 на 7 тоже равен 3. 10 - это и есть число , равноудаленное от 7 и 13. Особый случай представляет собой тройка чисел 3, 7 и 11. Скажем, остаток от деления 7 на 3 раввен 1, а 11 на 7 - равен 4. Но мы можем записать 7=2·3+1=3+4, То есть и здесь приходим к одинаковому остатку 4. Существование одинаковых остатков следует из элементарных положений теории делимости и условия m

Смотрите также публикации assembler.ru, предшествовавшие появлению данного документа:
Инструкция оксикардин
Препарат Cardiotonic Pills (торговое название "Оксикардин") производства фармацевтической компании "Тяньшили Груп" (КНР) - это сердечно-сосудистый антиангинальный препарат, применяемый для лечения стенокардии. Здесь публикуется Инструкция (информация для специалистов) по медицинскому применению препарата "Оксикардин" (Oxycardin).
Предупреждение. Настоящая публикация не является ни официальной, ни рекламной. Текст документа получен из неофициального источника. Достоверность документа перед публикацией не проверялась. Сведений о том, где и каким образом можно получить официальную версию документа, мы не предоставляем. Также мы не предоставляем сведений о том, где и каким образом можно приобрести препарат.
Единственной целью публикации является предоставление информации, содержащейся в документе всем, кому эта информация может понадобиться. (Именно такая проблема однажды встала перед нами, и все поиски в Интернет оказались безуспешны.)
См.также:
| ОДОБРЕНО Фармакологическим Государственным комитетом «13 » января 2000 г. |
ИНСТРУКЦИЯ
(информация для специалистов)
по медицинскому применению препарата
ОКСИКАРДИН (Oxycardin)
Регистрационный номер: П № 011763/01-2000
Торговое название препарата: ОКСИКАРДИН (Oxycardin)
Состав и лекарственная форма: Пеллеты, содержащие
Фармакологические свойства
Фармакотерапевтическая группа: сердечно-сосудистое средство. Антиангинальный препарат.
Фармакодинамика: препарат обладает антиангинальным действием. Основной механизм действия препарата заключается в расширении коронарных кровеносных сосудов, увеличении коронарного кровотока, снижении потребления кислорода миокардом.
Показания к применению. Купирование и лечение стенокардии.
Противопоказания. Повышенная чувствительность к компонентам препарата. Беременность и период кормления грудью. Детский возраст.
Особые указания. Не рекомендуется принимать препарат непосредственно перед сном.
Взаимодействие с другими лекарственными средствами. Не выявлено.
Способ применения и дозы
Для купирования приступа стенокардии рекомендуется прием 10 пеллет сублингвально. Для лечения приступов стенокардии принимать Оксикардин по 10 пеллет сублингвально 3 раза в сутки. Курс лечения составляет 4 недели. Повторные курсы - по рекомендации врача.
Побочное действие. Возможны аллергические реакции к компонентам препарата, головная боль, головокружение, нервное возбуждение, нарушение сна.
Форма выпуска. Пеллеты по 25 мг.
Условия хранения. Хранить в сухом месте, в герметичной упаковке при комнатной температуре. Хранить в недоступном для детей месте.
Срок годности. 4 года. Не использовать позже даты, указанной на упаковке.
Условия отпуска из аптек. По рецепту врача.
Производитель. Фармацевтическая компания " Тяньшили Групп" Лтд, КНР, г. Тяньцзинь.
Эффективность препарата оксикардин
Препарат Cardiotonic Pills (торговое название "Оксикардин") производства фармацевтической компании "Тяньшили Груп" (КНР) - это сердечно-сосудистый антиангинальный препарат, применяемый для лечения стенокардии. Здесь публикуется документ Изучение клинической эффективности и безопасности антиангинального препарата "Оксикардин".
Предупреждение. Настоящая публикация не является ни официальной, ни рекламной. Текст документа получен из неофициального источника. Достоверность документа перед публикацией не проверялась. Сведений о том, где и каким образом можно получить официальную версию документа, мы не предоставляем. Также мы не предоставляем сведений о том, где и каким образом можно приобрести препарат.
Единственной целью публикации является предоставление информации, содержащейся в документе всем, кому эта информация может понадобиться. (Именно такая проблема однажды встала перед нами, и все поиски в Интернет оказались безуспешны.)
См.также:
Московская медицинская академия им.И.М.Сеченова
Кафедра клинической фармакологии и фармакотерапии ФППО
Руководитель клинических исследований: академик Альбинская Л.И.
Российская академия медицинских наук
Проблемная лаборатория по разработке , изучению, внедрению,
производству и маркетингу лекарственных средств
Руководитель клинических исследований: профессор Кукес В.Г.
Московский государственный
медико-стоматологический университет
Кафедра внутренних болезней № 5
Руководитель клинических исследований: профессор Задионченко В.С.
ОТЧЕТ
ИЗУЧЕНИЕ КЛИНИЧЕСКОЙ ЭФФЕКТИВНОСТИ И
БЕЗОПАСНОСТИ АНТИАНГИНАЛНОГО ПРЕПАРАТА
«ОКСИКАРДИН»
Производства фармацевтической компании"Тяньшили Групп", КНР
Москва, 1999
По дневникам самонаблюдения больного расчитывалось количество приступов стенокардии и потребность в нитроглицерине за неделю.
Критерии эффективности
Оценка безопасности и переносимости
В ходе исследования регистрировались все нежелательные явления с отметкой времени их развития, продолжительности, интенсивности, связи с лечением, исход.
Критерии оценки переносимости:
РЕЗУЛЬТАТЫ ИССЛЕДОВАНИЯ
Под наблюдением находилось 75 больных ИБС со стенокардией 1-Ш ФК в возрасте от 42 до 72 лет (средний возраст 57,4±7,3 года), мужчин - 43, женщин - 32.
Доза ОКСИКАРДИНА составляла 250 мг 3 раза в день сублингвально.
При недостаточной эффективности к концу 1-ой недели лечения дозу увеличивали до 375 мг 3 раза в день.
Все пациенты давали устное информированное согласие на участие в исследовании и выполнение его требований.
Изучение антиангинальной эффективности ОКСИКАРДИНА по дневникам самонаблюдения больных показало, что уже через 1 неделю терапии большинство пациентов отмечали значительное улучшение общего состояния, повышение работоспособности, появление бодрости, лучшую переносимость повседневных нагрузок, многие из них перестали "чувствовать сердце", "забыли о сердце". Большинство больных выразило желание продолжить лечение.
Через 1 неделю непрерывной терапии ОКСИКАРДИНОМ потребность в нитроглицерине снизилась на 52,2%, а через 4 недели - на 63,7%.
При приеме ОКСИКАРДИНА сублингвально для купирования приступа стенокардии отмечена возможность купирования приступа при приеме одномоментно 10 пеллет, но скорость купированна была ниже, чем при приеме нитроглицерина. В то же время действие ОКСИКАРДИНА было более мягким, не сопровоящалось головной болью.
Результаты ВЭТ дают объективное подтверждение высокой антиангинальной и антиишемической эффективность ОКСИКАРДИНА.
К концу 1-ой недели лечения отмечена тенденция к увеличению пороговой мощности нагрузки и объема работы. Продолжительность нагрузки возросла на 23,4%, степень депрессии сегмента ST уменьшилась на 41,5%.
К концу 4-ой недели непрерывного лечения ОКСИКАРДИНом
По данным суточного мониторирования ЭКГ через 4 недели лечения количество эпизодов депрессии сегмента ST уменьшилось на 75,4%.
В целом, хорошая антиангинальная и антиишемическая эффективность ОКСИКАРДИНА у больных ИБС, стенокардией напряжения 1-Ш ФК составила 67,8%.
В процессе непрерывного 4 недельного лечения ОКСИКАРДИНОМ не было обнаружено существенных изменений средних значений ЧСС, САД и ДАД. Это делает возможным применение ОКСИКАРДИНА у пациентов как с высоким, так и с низким артериальным давлением.
Индивидуальный анализ показал, что у 6 больных в конце 1-ой недели приема ОКСИКАРДИНА ЧСС увеличивалась на 15-20 ударов в минуту, однако к концу 4-ой недели степень прироста ЧСС уменьшилась и составила 4-6 ударов в минуту.
По данным ЭхоКГ достоверных изменений показателей сократительной функции миокарда левого желудочка под влиянием ОКСИКАРДИНА отмечено не было, но имелась тенденция к уменьшению его объемов в систолу и диастолу.
При сублингвальном приеме препарата у 9 больных (12,0%) отмечено возникновение следующих побочных эффектов:
Возникшие нежелательные явления проходили при переходе на пероральный прием ОКСИКАРДИНА или после уменьшения дозы до 5 пеллет на прием и ни в одном случае не потребовали досрочного прекращения лечения.
Таким образом, хорошая переносимость ОКСИКАРДИНА отмечена у 66 больных (88,0%), удовлетворительная - у 9 (12,0%).
ЗАКЛЮЧЕНИЕ
Терапевтическая эффективность ОКСИКАРДИНА составила в целом 67,5% (58,0% при монотерапии и 76,9% при сочетании ОКСИКАРДИНА с нитратами пролонгированного действия).
Побочные эффекты отмечены в 12,0% случаев при сублингвальном приеме и не возникали при пероральном приеме ОКСИКАРДИНА.
ОКСИКАРДИН (фирма «Тяньшили Групп», КНР) может быть рекомендован для широкого медицинского применения в качестве антиангинального и антиишемического средства при лечении больных ИБС со стенокардией напряжения 1-Ш ФК в виде монотерапии или в комбинации с другими препаратами.
Как починить hewlett packard
Допустим, у вас есть Hewlett Packard SureStore 7200. Это такой внутренний пишущий дисковод CD (он же - CD-recorder, CD-writer) для IDE-интерфейса, способный работать как с CD-R, так и с CD-RW. Старенький. Служит вам честно больше двух лет. Отливает в золоте плоды вашего творчества. Любуетесь вы, как растет стопка архивных дисков, и теплеет на душе: не пропадет ваш скорбный труд, а достанется истории в целости и сохранности.
Впрочем, если у вас есть что-нибудь другое от того же производителя, принтер там или даже компьютер, тоже неплохо. Много разной полезной техники производит Hewlett Packard. Бранднэйм - он и есть бранднэйм.
И вот пишете вы как-то раз очередной свой золотой диск, и случается с вами то, чего больше всего боятся все владельцы пишущих сидюков: выскакивает buffer underrun.
Маленькое техническое пояснение. Технология записи компакт-дисков такова, что данные на запись обязательно должны поступать непрерывным потоком. Это очень просто понять, если представить себе, как бежит луч пишущего лазера по записываемому слою вращающегося диска-матрицы, прочерчивая на нем спиральную дорожку, от центра к периферии. Позиционирование осуществляется простым перемещением луча вдоль радиуса диска. Приостановиться, вернуться назад, попытаться повторить запись невозможно - это вам не винчестер, где есть разметка секторов, позволяющая точно позиционировать необходимый блок информации. Любое нарушение в потоке записываемой информации ведет к краху процесса записи и безвозвратной порче матрицы (если, конечно, это одноразовый CD-R, а не многоразовый CD-RW).
Для обеспечения непрерывности потока записываемых данных внутри пишущего дисковода имеется специальный буфер памяти (в вашем случае - 768 Кбайт). Данные в него подгружаются порциями, по мере необходимости, из внешнего источника через дисковый интерфейс IDE, а считываются непрерывно, в темпе записи на диск. Естественно, главной заботой всей системы в течение всего процесса записи должно быть поддержание буфера в наполненном состоянии. Если буфер в какой-то момент хотя бы на мгновение окажется пуст - запись прервется со всеми вытекающими печальными последствиями. Это и есть та самая ситуация, которая называется buffer underrun.
Обычная причина опустошения буфера - это недостаточность скорости источника данных. Допустим, вы пишете компакт-диск на двухкратной скорости, что означает, что необходимо обеспечить скорость входного потока данных 300 Кбайт/с. А в качестве источника выбрали старенький сидишник от Panasonic тоже с двухкратной скоростью чтения. Казалось бы, все получается. Ан нет. Вы забыли, что при чтении компакт-дисков довольно часто возникают ошибки, для устранения которых дисковод-источник пытается считать информацию оригинала повторно, для чего, в свою очередь, долго позиционирует читающий луч. Пока он этим занимается, буфер пишущего дисковода неумолимо опустошается, и в конце концов все заканчивается катастрофой. То же самое следует ожидать в случае, когда источник вы выбрали подходящий (например, винчестер), но при этом загрузили систему какой-нибудь параллельной работой. Пишущий дисковод, например, чувствует, что пора бы подгрузить буфер, обращается к системе, а она в это время занята расчетом зарплаты вашего соседа за прошлый год с целью проверки обоснованности покупки им подержанной "копейки". И амба.
Для того чтобы оценить, насколько подходит предполагаемый источник данных для записи на CD, имеются разные средства. Например, в поставляемом с дисководом приложении Adaptec Easy CD Creator их два: тест Transfer rate и тест, имитирующий запись. Первый служит для предварительной оценки пригодности источника данных и представляет собой замер скорости поступления данных при чтении некоторого тестового количества файлов. При этом отдельно оценивается скорость при чтении маленьких и при чтении больших файлов. Первая обычно несколько меньше, чем вторая, потому что маленькие файлы требуют дополнительных накладных расходов файловой системы на позиционирование головок дисководов. Выполнять тест Transfer rate следует нечасто. Обычно достаточно сделать это один раз для каждого из потенциальных источников данных. Желательно, чтобы скорость источника данных как минимум в два раза превышала скорость записи. Имея, например, пишущий дисковод с четырехкратной скоростью записи (600 Кбайт/с), вам придется быть готовым к проблемам, если вы используете в качестве источника старенький винчестер с оценкой по тесту Transfer rate что-нибудь около 1000 Кбайт/с. А вот используя более современный винчестер с 3000 Кбайт/с и более, вы можете чувствовать себя более-менее спокойно.
Не забывайте только еще об одном обстоятельстве. Некоторые винчестеры, давая в целом очень неплохие характеристики по тесту Transfer rate, имеют привычку время от времени задумываться о чем-то своем, сокровенном. В частности, грешат этим некоторые модели от Maxtor. О том, что вам попался именно такой задумчивый винт, вы, возможно, догадаетесь по куче испорченных матриц.
Второй тест следует выполнять каждый раз перед записью. Он, по идее, заключается в имитации процесса реальной записи реального набора ваших данных, за тем небольшим исключением, что при этом не включается пишущий лазер. (Сказано "по идее" потому, что практика показала, что это не совсем так. Но об этом ниже.) Нормальное пишущее приложение, вроде уже упомянутого Adaptec Easy CD Creator, по умолчанию предлагает вам именно такую комбинацию: тест, а потом запись. И это правильно, потому что скорость потока данных очень сильно зависит от того, что этот поток собой представляет: то ли один файл размером 100 Мбайт, то ли 100000 файлов по 1 Кбайт. В последнем случае даже на быстром винчестере можно запросто столкнуться с ужасным buffer underrun'ом.
Резюмируя сказанное, следует дать несколько полезных советов:
Однако вернемся к вашему Hewlett Packard SureStore 7200. Источником данных служил для него жесткий диск от Seagate. Имитирующее тестирование прошло нормально, что настроило вас на благодушный лад. И вдруг, записав чуть больше 5000 блоков, дисковод вываливается в buffer underrun! И просительно выдвинутый трей достаточно прозрачно намекает на предстоящие вам незапланированные траты…
Хорошо, что наученные предыдущим горьким опытом общения с неким угрюмым детищем фирмы Pinnacle еще в те времена, когда чистая золотая матрица стоила 10 баксов, вы, прежде чем дописывать на уже использованный диск новую сессию, предварительно сохранили все его предыдущее содержимое на винчестер. Иначе был бы безвозвратно утрачен труд двух предыдущих месяцев.
Поначалу вам показалось, что ничего особенно страшного не произошло. Ну запоролась матрица, эка невидаль. Возьмем другую. Сейчас их по доллару за штуку найти - не проблема. Но на всякий случай вы решили поэкспериментировать. Взяли перезаписываемый диск. И давай пытаться писать на него. Из пяти попыток три закончились крахом. Попробовали писать другие данные. Не помогло. Переинсталлировали софт. Тот же эффект. Вот именно на этом этапе вы ощутили некий неприятный холодок между лопаток.
Дальше пошли эксперименты с железом. Вскрыли брюхо компьютеру и, наигравшись с комбинациями Master-Slave, в конце концов отключили висящий на том же интерфейсе второй CD-ROM. Без толку. Вынули страдальца из этого компьютера и запихнули его в другой. На том очень кстати оказались проинсталлированы Windows 95 и NT4.0. Ничего не помогло. И только тут вы, наконец, сознались себе в том, что ваш любимый HP SureStore 7200 гавкнулся.
Что делать (как спрашивали друг друга Н.Г.Чернышевский и В.И.Ленин)? Естественно, чинить. Причем за деньги, потому что годовая гарантия давно истекла.
Вы лезете в Интернет, и без напряжения находите на каком-то сайте московский телефон технического центра Hewlett Packard: 797-3520. Звоните туда. Милый механический голос предлагаем вам нажать в тональном режиме кнопочку "1". Все еще надеясь на лучшее, вы выполняете указание и, бродя по виртуальному телефонному меню, в конце концов добиваетесь от механической барышни адреса центра: Спартаковская,16.
Кроме того, вы получаете возможность побеседовать с живой девушкой- оператором. На ваш лепет про buffer underrun, про проверку на двух компьютерах и на трех ОС и прочие ваши метания девушка отвечает, что сиди-райтеры они не чинят, но с удовольствием после тестирования заменят его вам целиком. Вы радостно прикидываете, сколько сэкономите, если получите новый, современный пишущий сидюк с вычетом стоимости почти исправного старого, и мчитесь через всю Москву на Бауманскую, в районе которой расположен ваш благодетель - техцентр Hewlett Packard. Оглушенный счастьем, вы при этом совершенно не обращаете внимания на интонацию, с которой любезная девушка произносила свои любезные слова. А ведь, задумавшись, вы могли бы без труда припомнить, что точно такую же интонацию вы слышали совсем недавно, не далее как в прошлую субботу, покупая на рынке черешню. На вопрос о том, есть ли в черешне червяки, здоровенная тетка отвечала: "Да нет, вроде," - как будто сама видела эту свою черешню в первый раз. И слово "нет" было произнесено с той самой интонацией.
Прибыв на место, вы долго бродите вокруг полуразвалившегося здания дореволюционной постройки, расположенного по адресу Спартаковская, 16, вызывая нездоровый интерес у толпы бауманских бомжей и охранников какого-то офиса без вывески, но зато с телекамерой. И только напрягши все свои умственные способности и еще раз созвонившись с любезной девушкой, вы наконец понимаете, что оказались на Спартаковской улице, а вам нужна Спартаковская площадь. Видно, здорово наследил древнеримский пугачев в Москве, если в его честь назвали и улицу, и площадь, и еще переулок.
Преодолев метров семьсот между Спартаковскими 16, вы, наконец, находите вожделенную синюю вывеску Hewlett Packard. Техцентр оказывается расположенным в современном деловом здании, инкрустированном ценными породами мрамора и, вероятно, евросантехникой. Порадовавшись за сотрудников техцентра, вы одновременно понимаете, что аренду этого архитектурного великолепия частично придется оплатить и вам.
И вы входите в прохладный кондиционированный сумрак обиталища отвертки и паяльника. Первое, что бросается в глаза прямо от входа - выгородка с надписью "Касса". Тут вы вспоминаете, что телефонный робот, разъясняя вам расписание работы техцентра, особо подробно, тщательно, со смаком, подавал расписание работы кассы. И еще почему-то вспоминаете червивую черешню.
Недалеко от кассы вы обнаруживаете несколько столов, за которыми удобно расположились несколько же симпатичных молодых людей мужского пола, на каждом из которых написано "Hewlett Packard". Решив, что именно эти люди заберут у вас сейчас ваш старый барахлящий сидюк и вынесут из недр своего волшебного замка новый, прекрасный, перевязанный синей ленточкой, вы мчитесь прямо к ним. Правда, молодые люди оказываются несколько заняты с уже сидящими перед ними клиентами, и вам приходится ждать, в отсутствии свободного стула переминаясь с ноги на ногу и прислушиваясь к разговорам.
Один из молодых людей беседует с клиенткой - дамой среднего возраста из числа тех, на которых держится любая организация. Дама нервно гоняет по столу образцы печати струйного принтера и пытается втолковать своему визави, что конкретно не устраивает ее в цветопередаче. Видно, что молодой человек страшно озадачен, потому что он молча берет в руки то один листок, то другой, так и сяк рассматривает их и кладет обратно. Этим он продолжает заниматься все оставшееся время вашего пребывания в техцентре.
Другой молодой человек уже заканчивает беседовать с клиентом. Окончание беседы еще более укрепляет ваши подозрения, потому что представляет собой заполнение каких-то бумажек с многими графами и проставление каких-то росписей. И вы опять понимаете, что речь идет о деньгах.
Наконец последняя роспись проставлена, и молодой человек обращает свое благосклонное внимание на вас. Вы усаживаетесь перед ним и изливаете свои жалобы, подпихивая в сторону молодого человека железного виновника своих несчастий. Но молодому человеку не нужен ваш дисковод. Он даже не смотрит в его сторону. Он внимательно смотрит на вас и спрашивает: "Но вы знаете, что это будет стоить дорого?" Вы спрашиваете: "Сколько?", думая при этом про себя: "А-а-а, черт с ней, с сотней баксов!" Но молодой человек не торопится отвечать на ваш глупый вопрос. Он долго щелкает клавишами компьютера, объясняя вам при этом ваши права, как будто он никакой не приемщик в радиомастерской, а настоящий американский коп. Он говорит вам, что сначала они возьмут у вас дисковод на тестирование. И если окажется, что он действительно неисправен, то они будут иметь с вами дальнейший разговор. А если он окажется исправен, то вернут они его вам только взамен шестидесяти баксов. За беспокойство, надо понимать, потому что за шестьдесят баксов можно протестировать не то что дисковод, а целый "Мерседес", и еще на "Жигули" останется. Но вот если дисковод действительно окажется неисправен (а это скорее всего так, говорит молодой человек, уверенный в технике своей фирмы), то вы сэкономите эти самые шестьдесят баксов, потому что тогда у вас никто их не возьмет. Тогда вам предложат замену с трехмесячной гарантией. И эта замена будет стоить… Будет стоить эта замена… Замена эта стоить будет… Стоить замена будет эта… Триста двенадцать долларов двадцать центов.
"Спасибо," - говорите вы, укладываете железяку в бесполезный антистатический пакет, прячете пакет в чемодан и позорно ретируетесь из техцентра, бросая на поле боя погибших и добивая раненых.
Дома вы снова залазите в Интернет, к Павло Дзиковскому, и обнаруживаете, что по всей Москве продается четырехскоростной HP SureStore 8250, и красная цена ему - 200 долларов. И в тот же день вы едете в давно уважаемую вами фирму НАК, что в Лебяжьем переулке, в ста шагах от кремлевской стены, и покупаете там новый прекрасный дисковод, даже дешевле среднестатистической. Естественно, этот самый, от Hewlett Packard. А как же иначе: бранднэйм - он и есть бранднэйм.
Классификация информации
Очевидно, что вся информация, доступная человечеству к началу третьего тысячелетия от Р.Х., делится на три больших класса:
Чрезвычайно интересно, не останавливаясь на семантическом уровне приведенной выше классификации, проследить деривационные, и, не побоюсь этого слова, педигриальные свойства генезиса каждого из ее разделов. Ибо естественно, что прямо из этих коренных свойств проистекают качественные характеристики текущего состояния информации соответствующего класса.
Начнем, с информации класса (c), на сегодняшний день наименее структурированной и упорядоченной. Как показывают многочисленные исследования (см., например, фундаментальный труд проф.М.Л.Кнопковича "Эпидемия ящура в Саратовской губернии в 1903 году"), самым значимым источником этой информации во Вселенной является неживая природа. Основной объем т.н. c-информации образовался одновременно со Вселенной в момент Большого взрыва, и на всех последующих этапах ее жизни возрастал незначительно, одновременно подвергаясь постоянной энтропийной эрозии и, в силу этого, деградируя качественно.
Если ограничиться рамками планеты Земля, то в ее истории обнаруживаются два отчетливых генерационных всплеска c-информации. Первый произошел в третьей четверти мелового периода, на исходе мезозойской эры, и именно он, по результатам последних исследований, явился причиной гибели динозавров. (Здесь надо отметить, что, в отличие от Вселенной в целом, на Земле существенную роль в генерации c-информации играет присутствующая здесь живая природа.) Меловой с-всплеск, как выяснилось, объясняется чрезмерной плодовитостью динозавров в условиях благоприятного климата той эпохи. По нашим подсчетам, жившее тогда на Земле количество особей, имевших вес свыше полутора тонн, превышает 17 миллиардов. Что же говорить об их более легких собратьях! Каждый экземпляр динозавра имел до сорока только ближайших родственников, а число двоюродных достигало тысячи и более. Представляете, что творилось в период предрождественских распродаж? А дни рождения? А, наконец, 8 марта? Собственно, необходимость помнить все эти даты и породила тот невероятный объем c-информации, оказавшийся непосильным для слабого мозга пресмыкающихся, и, в конечном итоге, погубивший их.
Второй всплеск, как может догадаться читатель, происходит в современную эпоху, и виновником его является, к сожалению, человек. Длительное время казалось, что праздники и юбилеи не угрожают человечеству. Дело в том, что человек, более ментально продвинутый, чем динозавры, нашел мощное защитное средство против информационной угрозы такого рода. В ходе эволюции примерно половина человечества, впоследствии определившаяся как мужчины, ценой многих лишений выработала уникальное свойство забывать памятные даты своих родных и товарищей, благодаря чему угроза гибели человечества была отложена. Но, оказывается, не отведена вовсе! В один прекрасный момент на арену информационной борьбы выступил интернет, с его бессчетными download-серверами, и оказалось, что до сорока процентов их ресурсов отведено под многочисленные программы, главной целью которых является напоминание пользователю о необходимости поздравить одну или несколько особей с каким-либо праздником. С этого момента выживание человечества снова встало под вопрос. Но эта тема, к сожалению, не является предметом нашего нынешнего исследования.
Информация класса (b) несколько более цивилизованна, если можно так выразиться, так как основным ее источником является разумное человечество, а точнее, его наиболее передовая часть, называемая "корпорация Microsoft". Значительно меньший b-объем генерируется рядом других корпораций, фирм и физических лиц. B-информация существенно более качественна, чем c-информация, имеет приятную сметанообразную консистенцию и оригинальный привлекательный аромат, легко и полностью усваивается организмом, особенно со второй попытки.
И именно в этом состоит ее опасность. До появления корпорации Microsoft человечество было вынуждено довольствоваться в основном грубой и плохо переваривавшейся c-информацией. Но c-информация хорошо очищала зубы от отложений зубного камня, держала в тонусе кишечник, стимулировала выработку слюны и желудочного сока. Массовая замена ее на b-информацию привела к катастрофическому зашлаковыванию организмов, ослаблению деятельности эндокринных желез и, как следствие, общему снижению стойкости организма к болезнетворным микробам и вирусам. Наиболее яркий пример последствий появления корпорации Microsoft - это пандемия СПИДа. На этом, пожалуй, можно остановиться в описании опасностей b-информации. Употребленная в разумных пределах она, как и все остальное, полезна, поэтому мы с удовольствием включаем ее в наши статьи.
И, наконец, информация класса (a). Наивысшая, без сомнения, категория информации. Недаром столько благородных a-созвучий находим мы вокруг себя. "А-а-а", - первый крик ребенка и последний крик выпавшего из окна. На "а" начинается слово "ассемблер", причем как в русском, так и в английском языке. Самые уважаемые и богатые люди в нашей стране называются "чиновники категории А". Впрочем, довольно примеров. Перейдем к сути. A-информация - это концентрированный интеллект, порожденный высшими проявлениями разума, свободного от забот о бренном теле. Строжайшая аскеза, беспримерный добровольный отказ не то что от наследования виртуальных функций, но даже и от элементарных фигурных скобок, порождают в итоге информацию класса А. Читайте a-статьи! Получайте a-знания! Программируйте a-программы! Ур-а!
В заключение - историческая справка. Пожалуй, первым зафиксированным в истории генератором информации класса (a) (он же - "настоящий ассемблерщик") был популярный древнегреческий философ Диоген. (Кстати, именно поэтому традиционно самая высококачественная информация класса (a) хранится в бочках. Хотя и из бутылок она тоже бывает неплоха.)
Эстафету у Диогена переняли старцы Оптиной пустыни и Валаама. Впоследствии, правда, с победой Советской власти, секреты старчества и аскезы были утрачены, сменившись роскошью и процветанием докоммунистического общества. Последний достоверный факт старчества зафиксирован историками И.Ильфом и Е.Петровым в известной монографии "Золотой теленок". С тех пор, казалось, ассемблер и связанная с ним a-культура больше не возродится. Отрадно видеть, что мы ошиблись в своих пессимистических прогнозах. Подтверждение тому - сайт assembler.ru и его посетители.
Можно ли зарабатывать на жизнь shareware?
| Date: | 4-15 ноября 1999 | |||||
| Newsgroup: | alt.comp.shareware.programmer | |||||
| Subject: | Is it really possiable to make a living writing shareware? В самом ли деле можно зарабатывать на жизнь, разрабатывая shareware? |
|||||
| Участвуют: | Keluna Gerry Quinn Matt piddy Peter Tiemann Scott Kane Alun Jones
| DemonAlleyCat@email.msn.com | gerryq@indigo.ie mharp@one.net sar@atinn.net peter@preview.org manager@micronexus.com.au alun@texis.com
| | Indigo OneNet Communications News Hub Range bCandid - Powering the world's discussions Interpacket Group Inc. Texas Imperial Software
| |
if(dhtml){document.write(" Все сообщения: [+][-] Открывать: [несколько]");}
[+] KelunaВ Subject все сказано...
Alan [+] Gerry QuinnДа, и некоторые люди делают это. Однако подавляющее большинство shareware-авторов - не делает.
Я представляю собой в настоящее время довольно существенное исключение, так как собираюсь получить в этом году около $20000. Главным образом - от уже лицензированных версий, нежели от новых продаж.
Gerry Quinn http://bindweed.com
Puzzle / Strategy Games and Kaleidoscope for Windows Бесплатные пробные версии, без ограничения по времени
[+] MattПривет,
хорошо, но теперь у меня несколько вопросов
Итак, для $20K в год, сколько времени вы тратите в среднем в неделю, работая над shareware-проектами?
Кроме того, как долго вы работаете на рынке shareware?
-Matt [+] Gerry QuinnЧестно говоря, не так уж и много в настоящее время!
По-настоящему я начал работать в прошлом году, однако до этого я много лет писал игры в качестве хобби без какой-либо выгоды.
Gerry Quinn [+] piddyНет. [+] Peter TiemannВсе относительно... Я думаю, вполне реально предполагать, что проект shareware с одним программистом может дать вам $10К - $30К в год.
Сможете ли вы жить на эти деньги - другой вопрос.
Я не смогу. Но с другой стороны, если вы живете в Мексике или Индии, вы не только жили бы на эти деньги, но вы были бы БОГАТЫ!
-- regards, Peter Delphi Components & Reviews на http://www.delphifaq.com
[+] Scott Kane
Scott Kane MicroNexus Software Australia
Moderator comp.software.shareware.* Team Nevrona Member
Member - Oz-Dev Australia
[+] Alun JonesЭто довольно легко выяснить, если ответить на вопрос: можно ли зарабатывать на жизнь написанием книг.
Несомненно, можно, и вам наверняка известны люди, которые это делают. Это хорошая новость. Однако маловероятно, что любой выбранный наугад автор зарабатывает на жизнь только этим.
Лично я и моя жена действительно зарабатываем на жизнь продажей нашего shareware FTP-сервера. Кроме того, в будущем мы даже надеемся несколько расширить наш бизнес.
Однако, мы не зарабатывали на жизнь (и в некоторых случаях вообще ничего не получаем) от ранее выпущенного shareware.
Не существует никакого другого способа выяснить, можете ли именно ВЫ зарабатывать на жизнь разработкой shareware, кроме как писать shareware и затем пробовать зарабатывать с его помощью. И это путь со слишком большим количеством неизвестных.
Alun. ~~~~ Texas Imperial Software | Try WFTPD, the Windows FTP Server. Find it 1602 Harvest Moon Place | at web site http://www.wftpd.com or email Cedar Park TX 78613 | us at alun@texis.com. VISA / MC accepted. Fax +1 (512) 378 3246 | NT based ISPs, be sure to read details of Phone +1 (512) 378 3246 | WFTPD Pro, NT service version - $100. *WFTPD and WFTPD Pro now available as native Alpha versions for NT*
openall(false);
Приведенное выше обсуждение содержит мнения людей, позиционированных вне Рунета. Резюмировать их (с учетом дополнительной информации, которой предостаточно) можно примерно так:
Более развернутое обсуждение заглавной темы, а также прочих многочисленных аспектов этого благородного занятия современных вильгельмовтеллей можно найти в конференциях Usenet на английском языке:
До недавнего времени в некоторых из перечисленных конференций бушевали страсти, и активность достигала многих десятков сообщений в сутки. Виной тому была некая личность по имени Дорен Розенталь (Doren Rosenthal), владелец Rosenthal Engineering (San Luis Obispo, CA USA) и большой апологет ASP (Association of Shareware Professionals), который устраивал дикие флеймовые разборки. Однако в последнее время его присутствия в коференциях незаметно. Есть подозрение, что кто-то из его собеседников добрался-таки до него в оффлайне.
Существует еще некоторое количество конференций, в названиях которых присутствует слово "shareware":
|
alt.binaries.dominion.shareware alt.binaries.shareware alt.binaries.shareware.ibm.pc alt.comp.shareware.for-kids alt.comp.shareware.nettamer alt.no-advertising.files.shareware.win-95 alt.no-advertising.files.shareware.win-95.apps de.comp.shareware.entwicklung de.comp.shareware.misc fido.ger.shareware fido.shareware-ger it.comp.software.shareware tw.bbs.comp.shareware |
Что касается русскоязычных ресурсов, посвященных shareware, то самым ценным является Shareware Russia. Кроме статей по теме (в том числе "эпохальной" статьи Александра Каталова "Искусство shareware", вышедшей в 1998 году в Компьютерре), здесь имеется несколько mail-листов, организованных через сервис eGroups:
Общая активность в этих листах рассылки - более сотни сообщений в сутки. Обсуждаются практически все темы, так или иначе связанные с shareware, в том числе:
На сайте также имеется подборка ссылок на ресурсы Интернет, посвященные shareware и сервисы для шароварщиков.
О пользе монополий
Итак, Microsoft снова, теперь уже окончательно и бесповоротно, признана монополистом. 3 апреля 2000 года некто Томас Джэксон, федеральный окружной судья, про которого до сего дня никто дальше родной вашингтонщины не слыхивал, тронул святое. И добро бы по делу!
Допустим, собрались бы лидеры нации и решили, что Microsoft малость перебрала с ролью в мировой истории. Статьи в законе про это не подберешь, но, поскольку случай, мягко говоря, неординарный, можно на худой конец и 28-ю поправку к конституции принять: "Всякий Карфаген обязан быть разрушен". Бывало, и по куда более мелким поводам поправки принимали. Устроили бы сенатские слушания, побазарили - да и приняли. Все умные люди поняли бы и согласились.
Так ведь нет же! Вместо того чтобы решать проблему по существу, лицемерно подобрали повод. Повод, надо признать, довольно сомнительный.
Имела ли право Microsoft встраивать Explorer в Windows? То есть было ли встраивание монополистическим актом, направленным на ущемление прав конкурентов, или могло быть объяснено иными, более благородными соображениями? Давайте обсудим серьезно, не сбиваясь на панегирики или филиппики.
Вспомним не столь далекие годы, когда в прохладных машинных залах паслись стайки милых, как на подбор, девушек-операторов, день и ночь скармливавших ревущим считывателям колоды перфокарт. В то дремучее благословенное время IBM взяла, да и зарегистрировала торговую марку DOS, ибо появились магнитные накопители. А буква D, между прочим, в той ТМ была от слова "дисковая". Потому что тогда настала такая пора. Уходили в прошлое дырочки в бумаге, им на смену приходили магнитные домены. И дисковая подсистема становилась неотъемлемой частью ОС. Дело дошло до того, что некоторые решили, будто ДОС - это и есть ОС, а все остальное, пользовательский интерфейс, например - это нашлепки. Во всяком случае, представить себе современную ОС без встроенной файловой системы (дисковой в первую очередь, конечно же) можно только при наличии очень извращенного воображения (о сетевых ОС читай дальше).
Сегодня мы на другом, далеком витке, но все той же самой диалектической спирали. Наступает новая реальность: компьютеры сливаются в глобальный информационный континуум. Уходят в прошлое локальные ресурсы, приходят распределенные.
Это, конечно же, не значит, что локальные ресурсы когда-нибудь вымрут, как перфокарты, но они перестанут быть основой операционной среды.
Хотим мы того, или не хотим, но операционная среда каждого компьютера станет частью единой планетарной операционной среды. У этого процесса на сегодняшний день есть всего два серьезных препятствия: каналы связи и защита конфиденциальности. Серьезных, но не непреодолимых, так что ожидаемое слияние не так уж далеко.
Операционная система обязана ответить вызову времени. Не просто иметь встроенные сетевые средства, а гораздо более того: жизнь в сети должна стать ее сутью. И было бы странно, если бы пользовательский интерфейс, вторая неотъемлемая и главнейшая часть современной ОС, остался бы в стороне от этих перемен.
А ведь именно броузер - зачаточный, но, безусловно, базовый элемент, из которого в будущем должен вырасти пользовательский интерфейс интегрированной в мировую сеть ОС. И Microsoft неторопливо, но совершенно недвусмысленно двигалась по этому пути.
Первым шатающимся шагом на нем, как все помнят, был ублюдочный стиль веб в проводнике-95.
С точки зрения изложенного судебное решение о неправомерности интеграции броузера в ОС представляется как минимум некомпетентным, а называя вещи своими именами - бездарным и вредоносным.
Поговорим о более общих вещах.
Так ли уж негативен монополизм Microsoft?
В чем, собственно, состоит основной вред монополизма для общества? Отнюдь не в уничтожении соперников нечестными приемами: это с успехом проделывается и в немонопольных ситуациях (см. популярную передачу "Дорожный патруль", там через день во всех подробностях показывают аккуратно продырявленные головы предпринимателей). Главный вред монополизма - в угнетении общественного прогресса. Монополия не дает возникать и развиваться альтернативным решениям встающих перед обществом задач. Общество теряет выбор. А вместе с ним теряет потенциал выживания.
Кто не верит - см. вокруг себя. Стены вашего подъезда несут на себе письмена измученных подростковой гиперсексуальностью прыщавых юношей потому, что у вас в районе единственное ЖЭУ, и вам некому, кроме него, отнести свои денежки. И в телефоне вашем, срывая коннект, тарахтят наборные импульсы всей округи, потому что на сто окрестных домов у вас один-единственный "телефонный оператор" (взято в кавычки преднамеренно). И в стране вашей зарплата дворника зависит от решений стран-участников ОПЕК, потому что термин "естественная монополия" для вас почему-то более приемлем, чем, скажем, "дойный козел".
Несла ли Microsoft вред монополизма? Нет, нет, и еще раз нет. Наоборот! И это действительно уникальная ситуация. Наверное, ее можно объяснить только невероятным сочетанием личностных особенностей ее лидеров с особенностями материала, живого и бешено растущего, с которым они работали. И только этим сочетанием объясняются фантастические достижения корпорации.
(Джобс, между прочим, при тех же, а то и более благоприятных, условиях, с ситуацией не справился).
Microsoft, идеально эффективно используя для бутстрэпа свои гигантские интеллектуальные и финансовые ресурсы, перла вперед сама и тащила за собой весь технологический мир. И если она вламывалась на уже занятый кем-то рынок, то не ради подавления конкурентов, а принося с собой лучший продукт. И если перекупала у кого-то мозги, то не ради того, чтобы обескровить противника, а для предоставления талантам условий, позволявших реализоваться гораздо лучше, чем на старом месте.
Даже естественная для всякого нормального человека ненависть к чужому успеху в случае Microsoft была плодотворна. На этой ненависти выросли не только фирмы - целые отрасли и даже концепции. Чего стоит одна только идея независимого программирования, которая без Microsoft вряд ли бы заинтересовала хоть кого-нибудь, кроме десятка чудаков - социалистов.
И это фантастическое время уходит. Просто потому, что оно обязательно должно уйти. Природа не терпит ситуаций, когда выживание вида ставится в зависимость от одного конкретного фактора. А Microsoft объективно все более претендовала на роль такого фактора. Корпорация изменила мир - и мир изменился. Без Microsoft он не станет лучше или хуже. Пройдет несколько лет, и он просто станет другим.
Но все мы немножко будем скучать по детству, правда?
Итак, на вопрос, является ли компания Microsoft монополистом в классическом понимании этого слова, следует ответить: нет. На вопрос, подлежит ли компания Microsoft разделению, следует ответить: безусловно, да. И объяснить людям - почему. Так и для NASDAQ будет спокойнее, глядишь - еще и вырастет. Потому что биржевые индексы бесятся в условиях неуверенности инвесторов, а в условиях разумной прозрачной политики либо остаются стабильны, либо растут.
Описание препарата оксикардин
Препарат Cardiotonic Pills (торговое название "Оксикардин") производства фармацевтической компании "Тяньшили Груп" (КНР) - это сердечно-сосудистый антиангинальный препарат, применяемый для лечения стенокардии. Здесь публикуется Описание препарата "Оксикардин" (Oxycardin).
Предупреждение. Настоящая публикация не является ни официальной, ни рекламной. Текст документа получен из неофициального источника. Достоверность документа перед публикацией не проверялась. Сведений о том, где и каким образом можно получить официальную версию документа, мы не предоставляем. Также мы не предоставляем сведений о том, где и каким образом можно приобрести препарат.
Единственной целью публикации является предоставление информации, содержащейся в документе всем, кому эта информация может понадобиться. (Именно такая проблема однажды встала перед нами, и все поиски в Интернет оказались безуспешны.)
См.также:
Tianjin Tianshili Pharmaceutical Group Co. Ltd.
60 Huangwei Road, Hebei District, Tianjin, China. Post Code: 300142
Tel.: 022-26264418, 26286379 Fax: 022-26264418, 26359109
http://WWW.TIANSHILI.COM.CN e-mail: TRADE@TIANSHILI.COM.CN------------------------------------------------------------------------------------------------------------------------
Представительство в Москве: Тел/факс: 095-5827309, Тел.:095-4001902
Торговое название - "Оксикардин" (Oxycardin)
Фармакологические свойства
"Оксикардин" является антиангинальным лекарственным средством растительного происхождения комплексного действия, применяемого для предупреждения приступов стенокардии, уменьшения их количества и интенсивности при хронической коронарной недостаточности как в монотерапии, так и в комплексной терапии с другими антиангинальными средствами.
Препарат "Оксикардин" является новой лекарственной формой, изготавливается по новой технологии путем экстрагирования эффективных растительных составов. Результаты клинического исследования больных коронарной болезнью сердца и стенокардией доказали, что эффективность лечения препаратом высокая и равна 95,3%. После приема "Оксикардина" облегчаются или исчезают симптомы стенокардии, стеснение в груди, сердцебиение, одышка и прочие недомогания облегчаются быстро и явно.
Клинический эффект показывает, что данный препарат имеет сильное действие расширения коронарных кровеносных сосудов, увеличения коронарного кровотока, снижения потребления кислорода миокарда, уменьшает размеры ишемического поражения. Результаты современного исследования фармакологии показывают, что препарат тормозит перекисное окисление липидов. Обладает антиоксидантными свойствами.
Важной особенностью является способность тормозить агрегацию тромбоцитов и препятствовать образованию тромбов в крови. Препарат "Оксикардин" быстро всасывается слизистыми оболочками, при сублингвальном применении он почти сразу оказывает эффект.
В связи со сложным составом препарата и его комплексным воздействием, совмещающего в себе свойства как быстрого, так и пролонгированного действия, препарат может применяться как для длительной профилактики приступов стенокардии, так и для облегчения острых приступов и уменьшения их количества.
Эффективность препарата в сравнении с нитроглицерином приведена в таблице:
| Облегчение болевого синдрома |
Группа "Оксикардин" |
Контрольная группа Нитроглицерин |
||
| Время, мин. | Количество случаев | % | Количество случаев | % |
| 1-5 | 11 | 36,7 | 17 | 56,7 |
| 6-10 | 14 | 46,7 | 12 | 40,0 |
| 11-15 | 5 | 16,7 | 1 | 3,3 |
| Итого: | 30 | 100,0 | 30 | 100,0 |
Сравнительная эффективность препарата
с другими антиангинальными лекарственными средствами
В клиниках разных городов Китая были проведены широкомасштабные клинические испытания препарата "Оксикардин" для лечения хронической коронарной недостаточности и стенокардии различного типа. Исследования проводились как с группами сравнения, где использовались другие антиангинальные лекарственные средства, так и открытые испытания - без групп сравнения. Результаты испытаний приведены в таблице
| Группа "Оксикардин" | Группа сравнения Антиангинальные препараты | ||||||
| Кол. случаев, чел. |
Клинич. эффект. % |
Улучш. ЭКГ % |
Название препарата | Кол. случаев, чел. |
Клинич. эффект. % |
Улучш. ЭКГ % |
|
| 910 | 94,0 | 67,1 | Изозорбид динитрат | 763 | 77,6 | 49,3 | |
| 189 | 92,0 | 51,0 | Нитроглицерин | 117 | 94,6 | 52,0 | |
| 46 | 95,7 | 76,1 | Нифедилин | 32 | 75 | 43,8 | |
| 1258 | 95,4 | 56,0 | Сложн.таб.Даньшеня | 746 | 74,6 | 39,0 | |
| Итого | 2403 | 91,28 | 60,98 | - | 1658 | 78,25 | 44,61 |
| 995 | 89,91 | 59,73 | Без препарата сравнения | - | - | - | |
| Итого | 3398 | 95,30 | 60,36 | 1658 | 78,25 | 44,61 | |
Общие клинические исследования
Для клинических исследований было отобрано 157 больных хронической коронарной недостаточностью, стенокардией разного функционального класса. Все больные были поделены на 2 группы. Группа, принимающая "Оксикардин" -107 больных и группа сравнения -50 больных. Кроме того, в группу, принимающую "Оксикардин", были включены 22 больных стенокардией на фоне перенесенного инфаркта миокарда, 16 больных стенокардией и нарушением углеводного обмена, 38 больныхстенокардией и гипертонией.
Все больные группы "Оксикардин" и группы сравнения прошли общепринятые лабораторные исследования крови и мочи до и после приема препаратов, проводилось наблюдение за артериальным давлением, числом ударов сердца, режим работы сердечной мышцы регистрировался 12 канальной электрокардиограммой. Проводилось измерение в крови холестерина и триглицерида. Велось наблюдение за состоянием печени. Ежесуточное наблюдение велось за изменением симптомов стенокардии, числом приступов, состоянием пульса, картиной языка.
Прием препарата "Оксикардин" осуществлялся сублингвально по 10 штук 3 раза в день.
В группе сравнения осуществлялся прием другого антиангинального препарата растительного происхождения - сложные таблетки даньшэня. Кроме того, в обеих группах проводилось комплексная терапия с применением антиангинального препарата изозорбида динитрата (Isosorbide dinitrate). В 27 случаях при нестабильной стенокардии использовался только препарат "Оксикардин".
Эффективность лечения оценивалась следующим образом:
Результаты эффективности приведены в таблице:
| Число случаев | % | |||||
| Заметн. эффект |
Эффектив. | Неэффек. | Итого | Заметн. эффект |
Эффект. | |
| Группа А (Оксикардин) | 38 | 64 | 5 | 107 | 35,5 | 95,3 |
| Группа B (сравнения) | 10 | 28 | 12 | 50 | 20,0 | 76,0 |
Из таблицы 3 видно, что заметная эффективность и эффективность в группе "Оксикардин" выше, чем в группе сравнения.
Оценка эффективности лечения путем электрокардиографического исследования
Отклонения в режимах работы сердечной мышцы регистрировались электрокардиограммами.
Эффективность лечения оценивалась следующим образом:
| Число случаев | % | |||||
| Заметн. эффект |
Эффектив. | Неэффек. | Итого | Заметн. эффект |
Эффект. | |
| Группа А (Оксикардин) | 12 | 26 | 57 | 95 | 12,16 | 40,0 |
| Группа B (сравнения) | 3 | 12 | 28 | 43 | 7,0 | 34,9 |
Примечание: Обострений не наблюдалось.
Из таблицы видно, что заметно эффективный результат ЭКГ в группе, принимающей "Оксикардин" составляет 12,16%, в группе сравнения - 7,0%. Улучшение в группе "Оксикардина" - 40,0%, в группе сравнения - 34,9%.
Оценка эффективности лечения по определению
концентрации холестерина и триглицеридов
| Холестерин, ммоль/л | Триглицериды, ммоль/л | ||||||
| Город, клиника |
Число случаев, чел. |
До лечения |
После лечения |
t | До лечения |
После лечения |
t |
| Пекин, НОА | 27 | 5,19±0,17 | 5,09±0,94 | 0,699 | 1,68±0,16 | 1,43±0,09 | 2,269 |
| Тяньцзинь | 25 | 5,05±0,29 | 4,13±0,17 | 4,660 | 1,86±0,17 | 1,82±0,16 | 0,357 |
| Пекин | 28 | 5,21±0,35 | 5,24±0,51 | -0,101 | 1,85±0,20 | 1,84±0,23 | 0,101 |
| Итого | 80 | 5,15±0,16 | 4,84±0,20 | 2,521 | 1,80±0,10 | 1,69±0,21 | 1,300 |
Из таблицы видно, что после лечения липиды низкой плотности имеют тенденцию к снижению. Холестерин всех групп снизился в среднем на 0,92 ммоль/л, t = 2,521, триглицериды в среднем снизились на 0,11 ммоль/л, t=1,300.
Побочные действия
В группе, принимающей "Оксикардин" среди 107 больных в период приема препарата в одном случае после каждого приема пилюль появлялась тошнота, которая исчезала через 15 минут после приема. У одного больного появилось кратковременное ощущение полноты в голове, которое не влияло на непрерывный прием препарата. Исследование крови, мочи, функции печени до и после приема не имели отклонений от нормы.
ВЫВОДЫ:
Препарат "Оксикардин" является новой лекарственной формой, изготавливается по новой технологии путем экстрагирования эффективных растительных составов. Результат клинического исследования больных коронарной болезнью сердца и стенокардией доказал, что эффективность лечения препаратом высокая и равна 95,3%. После приема "Оксикардина" облегчаются или исчезают симптомы стенокардии, стеснение в груди, сердцебиение, одышка и прочие недомогания облегчаются быстро и явно.
Клинический эффект показывает, что данный препарат имеет сильное действие расширения коронарных кровеносных сосудов, увеличения коронарного кровотока, снижения потребления кислорода миокарда, уменьшает размеры ищемического поражения. Результаты современного исследования фармакологии показывают, что препарат тормозит перекисному окислению липидов. Обладает антиоксидантными свойствами.
Фармацевтическая компания "Тяньшили Групп", КНР, г.Тяньцзинь
Правильная история компьютера
Не помню, кто (как бы даже не сам Карл Маркс) как-то взялся распределить, на каком языке с кем разговаривать. Как распределил - тоже не помню. Помню только, что с врагом - на немецком.
С другом - на английском? С девушкой по вызову - на итальянском? С дамой сердца - на французском? С холодильным мастером - на финском? С товарищем по партии - на китайском? С соседом в самолете - на идиш? С соседом по камере - на русском? С продавцом на рынке - на азербайджанском?
Но что с компьютером нужно разговаривать именно на английском - это точно. По крайней мере не на русском.
А не слабо нам мысленно взмахнуть волшебной палочкой? Абак и астролябия, уж Бог с ними, пусть остаются на своих исторических местах. А вот, например, первый калькулятор пусть изобрел не Блез Паскаль в помощь отцу в 1642 году, а крепостной Федька Драный для учета телесных наказаний при Алексее Михайловиче "Тишайшем", и его за это случайно не запороли насмерть. И поэтому дальше покатилось!
Чарльзу Томасу Кольмарскому, допустим, утер нос наш Михал Василич Ломоносов. Пресловутого Бэббиджа заткнул за пояс, само собой, Колька Лобачевский - кому ж еще? Хермана Холлериха поставил на место, например Пал Карлыч Штернберг, который своевременно бросил валять дурака с астрономией и Мосреввоенсоветом, а занялся делом: основал товарищество на паях Международные Деловые Машины (далее - "МДМ").
Дальше все шло в том же духе. В Штатах, после неудачи с сухим законом, долго искали виновных, посажали-постреляли миллионов двадцать народу потолковей, потом посажали-постреляли тех, кто сажал-стрелял, ну и так далее. Во время войны американцы, вместо того, чтобы расчет траекторий стрельбы автоматизировать, Эккерта с Мочли вместе со всем Пенсильванским университетом как самых умных в заградотряды определили. А потом, в 46-м, вместо возни с ЭНИАКом они долго излечивались от космополитизма в благодатном юконском климате.
С фон Нейманом американцам совсем не повезло. Они его шлепнули еще в 41-м, за одну только приставку к фамилии. А Винер отсидел по полной за увлечение лженаукой "сибирьнетикой", про которую узнал из передач радиостанции "Рука Москвы". За КВ-приемник ему, кстати, еще червонец накинули.
Что случилось потом - общеизвестно. В 53- м наши гении сварганили БЭСМ. Потом за дело взялся штернберговский "розовый гигант" - МДМ. К концу 60-х весь машинно-счетный мир говорил на русском. Дольше всех, по обыкновению, сопротивлялись американцы: настойчиво употребляли неблагозвучную аббревиатуру ECM (произносится "э-кэ-эм") вместо простого и ясного русского слова "вычислятель" (пишется через "я", так как относится к неодушевленному предмету. Ср. "вычислительщик" - человек, работающий с вычислятелями).
Был еще такой эпизод. Два друга, молодые парни, Степан Попов и Степан Ямщик, в отцовском огороде слепили из отечественных комплектующих первый одночеловечный вычислятель, "Картошка-1". По сей день всему миру известен товарознак основанной ими компании - картофельный клубень со сгнившим боком.
В настоящее время лидирующее положение на вычислятельном базаре занимают такие товарищества, как Умник (известное в первую очередь своими оченьмаленькодвигалками), Малослабость (прогобеспный гигант), МДМ, Солнце, Урочище, Коммунистический Упаковщик и многие другие, составляющие российскую гордость в начале XXI века. По акциям этих компаний судят о состоянии наукоемких и высокотехнологичных отраслей экономики России, для чего в свое время был введен специальный индекс, называемый "Максимально Универсальный инДекс Акций Компаний".
В заключение этого краткого исторического обзора приведем небольшой чатлано-пацакский словарь по вычислятельной технике и электронике:
| неправильно | правильно |
| software freeware shareware hardware |
прогобесп хальобесп дешобесп желтовар |
| transistor diode lcd microchip |
треххвост двуххвост светодвуххвост малогрызок |
| laser | сувил (световое усиление посредством возбужденного испускания лучей) |
| streamer inkjet printer laser printer scanner |
струячечник чернильник сувильник последовательник |
| radio | поповолн |
| modem cd-rom motherboard joystick |
накладыватель-сниматель сообщения на несущую (термин сокращению не подлежит) пк-чтп (плотный круг - чтение только память) [censored] доска палочка наслаждений |
| mov cmp jmp call xor int push pop xchg |
двиг срав прыг звон исклили дерг засун высун сменка (прислал Roman Hady) |
| red hat linux pan-european windows outlook |
красношляпый ванякс ос им. петра первого выглядыш, выглядок (прислал Argus) выглюк (прислал Frigate) |
| .exe .bat .com .sys .htm .html |
.исп .пак .ком .сис .гтр .гтря |
| copyright | скопировано правильно (прислал Dmitry Aleksandrovich Gusev) копья справа (прислал Frigate) |
| download | низогруз (прислал Michael Meshanin) |
| debugger | дуст (прислал Vlad) обезжучиватель (прислал Mac Buster) долбагер (прислал Frigate) |
| abort, retry, ignore | нефиг, нафиг, пофиг (прислал Sergey V. Fuklev) |
| TrueType Font | шрифт для печати газеты "Правда" (прислал Сергей Елкин) |
| upgrade | вышесорт (прислал olimpiyam) |
| reboot | снова-ботинок (прислал olimpiyam) переботинок (прислал Alessio) |
| chkdsk | провдиск (прислал Sasha Efremov) |
| добавь свой перевод с неправильного на правильный: | |
Пролетая над миллионом баксов
Догадка Гольдбаха подтверждается не абсолютно строго, но абсолютно убедительно. Дальше будет приведено полное доказательство, это не шутка. Но сначала предыстория, вдруг кто-нибудь из посетителей перед десертом любит чего-нибудь сладенькое?
Вернее, предысторий две. Первая давняя. Говорят, что ей 258 лет. Так оно и есть.
| Леонард, брателло! В довесок к той цистерне бензина, которую я закинул тебе на прошлой неделе, отгоняю еще вагон селедочных голов, как договаривались. С бабками не тяни, переведи их на тот счет, который я тебе тогда у Муськи дал. Как у тебя дела? Не достает ли Починок? Ну, бывай, на той неделе свидимся. Твой кореш Христиан. 7.06.1742 P.S. Кстати, тут я давеча задумался: а не является ли всякое четное число суммой двух простых чисел? По-моему, так оно и есть. А ты как думаешь? |
Так (или примерно так) писал своему другу великому шведу Леонарду Эйлеру Христиан Гольдбах. Именно постскриптумом он и стал знаменит. Во всяком случае, в Grolier есть статья "Goldbach's Conjecture", а вот статьи "Goldbach" нет.
Загнал ли Эйлер селедочные головы, история умалчивает. А вот на догадку внимание обратил, и с Гольдбахом согласился, но доказательства этому утверждению так и не нашел. Чувствовал, что задачка тривиальная, все думал: как-нибудь; да, видно, некогда было, замотался.
Шли годы. Эйлер закончил свои дела в этом мире, Гольдбах тоже, но люди про догадку не забыли. Уж больно привлекательны такие задачи: на первый взгляд элементарные, а решение в руки не дается. А задумаешься чуть поглубже - и вот уже два мужика в халатах тащут тебя, болезного, по белому коридору на очередной сеанс электрошока.
С годами слово "Догадка" стало писаться с большой буквы, из уважения к труду многих поколений психиатров. Сначала еще теплилась надежда, что кто-нибудь успеет доказать ее раньше лоботомии, но с появлением компьютера угасла: люди стали стремительно разучиваться думать. Зато много народу с большим удовольствием стало гонять процессоры на предмет эмпирических проверок. Дошли до 1014 (вот это тема для диплома! Блеск!).
Некоторое оживление в историю Догадки привнесло появление научной криптографии, поскольку криптографы, будучи людьми ленивыми, очень полюбили простые числа и не захотели возиться со сложными. Но и криптографы, занятые бегом по кругу в поисках ядов и противоядий, приняли Догадку как должное и не стали очень уж надрываться ее подтверждением.
Так и дошло дело до наших дней.
Вторая предыстория - продолжение первой. В 1992 году греческий писатель Апостолос Доксиадис написал роман "Дядя Петрос и Догадка Гольдбаха" (Uncle Petros and Goldbach's Conjecture by Apostolos Doxiadis)
Роман начинается так: "В каждой семье есть своя черная овца - в нашей ею был дядя Петрос". Рассказчик - любимый племянник дяди Петроса. Семейство всегда считало Петроса неудачником. В начале рассказа племянник оглядывается назад, в годы своей учебы в средней школе в Афинах. Тогда его живой детский интерес вызывал эксцентричный дядя, отшельником живший в предместье и интересовавшийся только шахматами и озеленением. Племянник знал, что дядя Петрос когда-то был талантливым математиком, долгие годы учился и преподавал в университете в Германии и немного - в Кембридже. Однажды он приходит к дяде и объявляет, что хотел бы делать карьеру математика. Дядя Петрос предлагает племяннику решить одну математическую задачку. Если решит - то это будет означать, что математика действительно является его призванием. А если не сможет - то должен дать обещание навсегда забыть о математике. Племянник принимает условие, и, так и не найдя решения, по достижении совершеннолетия все-таки отправляется в университет в США в полной неуверенности в своем будущем математика. Его случайный попутчик, оказавшийся математиком, объясняет ему, что задача, заданная ему дядей, была ничем иным, как Догадкой Гольдбаха. Оказалось, что дядя потратил всю свою жизнь в поисках ее доказательства, но так и не нашел его. Во второй части романа читатель видит рассказчика вернувшимся в Грецию и требующим у дяди объяснений: почему тот задал ему неразрешимую задачу. И тогда мы добираемся до "Истории дяди Петроса Папахристоса", которую рассказчик подает нам в третьем лице. Это очаровательная история о математике и гениальности, о гордыне и навязчивой идее, которые все вместе составили жизнь дяди Петроса. Третья часть романа переносит нас в наши дни. Петрос теперь 80-летний старик, и слегка не в себе. Племянник, все-таки начавший свою математическую карьеру, но впоследствии оставивший ее, вновь пытается получить от дяди ответ на вопрос, который не получил когда-то. И далее следует очаровательный конец повествования с великолепной кульминацией. Доксиадис - удивительный рассказчик. Он побуждает вас переворачивать страницы одну за другой, раскрывая вам историю и мир ученого-математика, всю свою жизнь целеустремленно преследовавшего единственную цель: подтверждение Догадки Гольдбаха. А это, между прочим, самая простая и самая трудная из догадок: каждое четное число, большее 2, представляет собой сумму двух простых чисел. Звучит элементарно, неправда ли? (© J.A, перевод assembler.ru)
В этом году роман, по случаю слегка переработанный автором, опубликован на английском языке сразу в США и в Великобритании, двумя издательствами - Faber and Faber Limited (Лондон) и Bloomsbury Publishing (Нью-Йорк). И для того, чтобы сделать потенциальному бестселлеру необходимую рекламу, кто-то из сотрудников того или другого издательства предложил гениальный ход: бросить челлендж ценой в пресловутый миллион баксов. Тогда казалось, что никто особо ничем не рискует (жалко будет, если человека теперь уволят), ведь над подтверждением Догадки Гольдбаха люди бьются сотни лет, и до сих пор даже не уверены, что таковое существует. Насчет увольнения сотрудника - шучу, конечно. Они ведь тоже не лыком шиты. Никто никого не уволит и никакого миллиона никто никому не заплатит, так что ежели кто уже точит фомку, чтобы вскрывать мой пуленепробиваемый сейф, оставьте железо в покое. Правила вызова составлены из того предположения, что потенциальные разгадчики загадки Гольдбаха живут исключительно либо на территории США, либо на территории Великобритании. Как будто если Догадку докажет не-американец или не-англичанин, то это как бы и не будет никаким доказательством. Ну да им виднее.
Так не доставайся ж ты никому! Этой публикацией я нарушаю все правила вызова, установленные издательством, но уж постараюсь сделать так, чтобы мой миллион было бы трудно присвоить кому-нибудь шустрому. Впрочем, все возможно. В правилах же не сказано, что миллион будет вручен автору. Там сказано, что миллион будет вручен выполнившему условия. Эй, резиденты США и Великобритании, для вас еще не все потеряно! В английском языке то, что я сейчас делаю, определяется идиомой "vsju malinu obossal, gad". Впрочем, шутки в сторону. Вот доказательство истинности Догадки Гольдбаха. Примечание от 30.05.00: На самом деле, то что вы можете прочитать далее - это только первый, несовершенный, вариант. Возможно, он будет полезен для понимания основных идей, но его недостатки не позволяют считать его настоящим доказательством. Настоящее сухое математическое доказательство здесь. Примечание от 27.07.00: Еще более сухое и, возможно, более корректное (или истинное, чем черт не шутит?) доказательство Догадки прислал Тим Туманный. Читайте!.
Еще раз повторю формулировку Догадки:
"Каждое четное число, большее двух,
может быть представлено
в виде суммы двух простых чисел."
Для начала поговорим о природе простых чисел. Всякому, кто когда-нибудь интересовался простыми числами, знакомо чувство удивления, которое вызывает видимая бессистемность их расположения в числовом ряду. Казалось бы, про ряд натуральных чисел люди должны знать все, ведь это самые первые числа, с которыми познакомились наши пращуры сразу после того, как однажды на охоте завалили не одного мамонта, а двух. И вот в этом незыблемом от Начала Времен монолитном строю, оказывается, полным-полно бойцов, которые стоят где хотят, и совершенно непонятно, как они там оказались.
Причем любой здравомыслящий человек, конечно же, понимает, что никакого хаоса нет, и вот это-то и раздражает больше всего. Если нет хаоса - значит, есть система. Видимо, сложная, но все-таки система. Так в чем же она?
Для ответа на этот вопрос, и раз уж мы обсуждаем натуральные числа, самое время вспомнить о кирпичах.

Нарисуем все потенциальные делители так, как показано, в виде кирпичной кладки.
Идея, по-моему, достаточно очевидна. Например, делитель "2" укладывается в кладке до отметки "13" шесть раз целиком, и еще пол-кирпича торчат наружу. А вот делитель "3" - четыре раза целиком и еще кусочек. И так далее.
Мы видим, что на отметку "13" попадает стык кирпичей в одном-единственном ряду - в ряду "13". Не нужно быть бакалавром математики, чтобы сообразить, что смысл этого рисунка, если исключить из рассмотрения избыточный нижний ряд, можно сформулировать следующим образом: "Если на отметку числа не попадает ни один стык кирпичей, то оно простое".
Теперь приглядимся к кладке повнимательнее. Посмотрите: все стыки в ряду "4" попадают на стыки в ряду "2"! Это значит, что если тестируемое число не попадает на стык в ряду "2", то уж на стык в ряду "4" оно точно не попадет! А мы, дураки, мучились, раствор мешали, на прораба матерились... Оказывается, ряд "4" совершенно неинтересен в смысле тестирования чисел на предмет их простоты. Убираем его нафиг. А вместе с ним и ряды "6", "8", "9", "10" и "12", так как все их стыки тоже надежно совпадают со стыками вышележащих рядов. Смотрим, что получилось.

Мама дорогая! Так это ж все сплошь простые числа! Вот оно, первое гениальное прозрение вольного каменщика: "Чтобы выяснить, является ли число простым, достаточно установить отсутствие его делимости нацело на все простые числа, меньшие его."
Уважаемые товарищи программисты! Не загружайте процессоры бесполезной работой! При определении простоты чисел используйте в качестве делителей только простые числа! (За исключением случаев, когда вы пишете на JavaScript. Тогда вы можете на этот призыв наплевать - все равно скрипт работает на чужом компьютере. Именно так я и сделал в этом примере.)
Впрочем, все вышесказанное никаких откровений в себе не содержит и давно всем очевидно. Не волнуйтесь: это всего лишь подготовка строительной площадки. А вот теперь, еще раз хорошенько всмотревшись в кирпичи, мы готовы сделать действительно принципиально важное для доказательства Догадки Гольдбаха умозаключение. Вот оно:
Простое число есть совокупный продукт
от всех простых чисел, меньших его.
Каждый ряд в кирпичной кладке - это периодический процесс с длиной периода, равной соответствующему ему простому числу. В точке 0 числовой шкалы узлы всех периодов совпадают. Очередное простое число образуется в точке, в которой периоды всех предыдущих простых чисел не имеют узлов. Следующее простое число образуется в новой точке, зависящей не только от всех предыдущих простых чисел, но и от вновь найденного.
Не удивительно, что ни на первый, ни на второй взгляд невозможно обнаружить системы в расположении простых чисел в числовом ряду! Ведь каждое простое число - это каша из всех предыдущих, тем более перемешанная и многокомпонентная, чем дальше от начала координат мы находимся.
Из этого тезиса можно делать разные полезные выводы, впрочем, имеющие отдаленное отношение к теме. Например, можно смело утверждать, что не может существовать никаких других строгих правил определения произвольного простого числа, кроме полного перебора всех простых чисел, меньших его.
И вот теперь, разобравшись с природой простых чисел, вернемся собственно к Догадке, и, конечно же, к кирпичам. Разве что раскрасим их теперь в какие-нибудь веселенькие цвета.

На этом рисунке слагаемое 11 покрашено в желтый цвет и представлено не само собой, а совокупностью формирующих его рядов кирпичей, соответствующих меньшим простым числам. Кирпичи, составляющие же слагаемое 5, покрашены в голубой цвет. Кроме того, начало кладки голубых кирпичей мы перенесли в точку "16", и направили кладку навстречу желтой. Видим, что имеет место точка рандеву, в которой встречаются отложенное слева число 11 и отложенное справа число 5. В этой точке, в соответствии с изложенным выше кирпичным взглядом на природу простых чисел, ни в одном ряду нет стыка кирпичей.

Почему сказано "вариант кладки" - потому что возможны и другие варианты, как показано на рисунке справа.
Давайте еще раз рассмотрим рисунок img03, ибо он позволяет сформулировать условие существования точки рандеву. Видно, что существует 15 точек, каждая из которых потенциально может стать точкой рандеву. Но всем, кроме одной, мешают стыки рядов кирпичей, иногда одного, а иногда и сразу нескольких. Разберемся с каждым из рядов по отдельности.
Правда, на рисунке img04 есть еще точка, отстоящая слева на 1, а справа - на 15, которой не мешает ни один стык кирпичей. Такая точка имеется у всех пар простых слагаемых, одно из которых - 3. Существование этой точки еще ждет своего объяснения, и, я думаю, кирпичная теория простых чисел способна дать его.
Видно, что желтый и голубой ряды кирпичей "2" синфазны. Их стыки совпадают. Это не удивительно, ведь число 16 кратно 2. Эти ряды будут совпадать и для любых других четных чисел. И они отсеивают половину всех потенциальных точек рандеву. Если бы фазы голубого и желтого рядов "2" не совпадали, тогда совместными усилиями они бы отсеяли все точки. Однако фазы рядов "2" не совпадают только у нечетных чисел.
А вы-то, небось, думали, что нечетное число не может быть суммой двух простых чисел, потому что сумма двух нечетных всегда четна? На самом-то деле причина в кирпичах!
А вот ситуация с желтым и голубым рядами "3" гораздо интересней. Поскольку число 16 не кратно 3, то фазы этих рядов не совпадают. В результате ряды "3" еще отнимают каждые две из трех потенциальных точек рандеву, оставшихся после разгрома, учиненного рядами "2". Очевидно, что после их совместных усилий остается вот какая доля потенциальных точек рандеву:

Следующий ряд - "5". К сожалению, на рисунке img03 ситуация не очень удачная - нет голубого ряда "5", потому что число 16 слишком маленькое. То же касается и ряда "7". Впрочем, мы уже достаточно подготовлены, чтобы забросить возню с кирпичами и обратиться-таки к абстрактным рассуждениям.
Если бы исследуемое четное число было побольше, и у второго слагаемого был бы голубой ряд "5", то он бы мог быть синфазен желтому (например, для числа 20), или несинфазен (например, для числа 22). Будем ориентироваться на худший для нас второй случай. Рассуждая аналогично рядам "3", можно считать, что несинфазные ряды "5" оставят только 3 из 5 точек, оставшихся после совместного воздействия рядов "2" и "3". То есть доля оставшихся потенциальных точек рандеву будет:

Аналогично для рядов "7":

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

Здесь:
Также очевидно, что прогрессия (4) бесконечно стремится к 0.
Сформулируем достаточное условие существования точки рандеву: "Для того, чтобы для некоторого четного числа Ej существовала хотя бы одна точка рандеву, достаточно, чтобы интерполированное значение прогрессии qi при Pi близком к половине Ej было не меньше, чем значение прогрессии:

Поясним, откуда здесь что.
(Ej-2)/Ej - это максимальная доля точек, которые не должны являться точкой рандеву для того, чтобы существование точки рандеву стало возможным. Наверное, проще пояснить на примере. Смотрим рисунок img03. Здесь Ej=16. Число потенциальных точек рандеву - 16-1=15. Но существование хотя бы одной точки рандеву возможно лишь в том случае, если не-"точками рандеву" будут не более 16-2=14 из них. То есть их максимальная доля составляет 14/16.
1-(Ej-2)/Ej - это, следовательно, минимально необходимая доля действительных точек рандеву (то есть доля одной-единственной точки).
Наконец, почему Pi должно быть близко к половине Ej? Да потому, что Догадка как раз про это: про четное как сумму двух простых. И еще вот почему.
Как уже было сказано, нас интересуют наихудшие для существования точек рандеву случаи. Если в этих случаях условие существования выполняется - то уж во всех других оно будет выполняться точно. Случай, когда простые слагаемые близки к половине составляемого ими четного числа, как раз из таких. Количество желтых и голубых рядов кирпичей в этих случаях примерно равно, в результате чего складываются наиболее благоприятные возможности для появления их несинфазности. Кстати, именно для таких, а вернее, для предельно худших случаев составлена прогрессия (4), потому что она вообще не предполагает наличия синфазных рядов (кроме, естественно, ряда "2").
Ну что ж, осталось совсем немного: убедиться, что достаточное условие существования точки рандеву действительно выполняется. Щелкайте - и убеждайтесь.
Убедились? Есть вопросы, да? Первый бросается в глаза сразу: почему в начальной части qi < ej? Ну что ж, если вы хотите проверить, что в этой части каждое четное число имеет пару простых слагаемых, просто посчитайте, это нетрудно (или поверьте предыдущим поколениям математиков). А насчет правоты достаточного условия - так оно ведь достаточное, а не необходимое. Здесь оно не работает по двум причинам. Во-первых, помните, что прогрессия (4) составлена в расчете на худший из худших случаев, когда голубых и желтых рядов - примерно поровну. Если вы учтете для первых нескольких чисел конкретные более благоприятные расклады (это выразится в замене в выражении (4) двойки на единицу в случае синфазности соответствующих рядов), весьма вероятно, все встанет на свои места. И во-вторых, на начальном участке велико влияние краевого эффекта, ведь наше достаточное условие в некотором роде - статистическое, и плохо работает на небольших выборках. Последнее утверждение, конечно же, образное. На самом же деле краевой эффект проявляется в том, что при небольшом числе младших простых чисел, определяющих слагаемые, на значение прогрессии (4) большое влияние оказывают дробные части от деления Ej на младшие простые числа (грубо говоря, части кирпичей, выступающих наружу).
Правда, насчет краевого эффекта - это просто догадка. Есть ли он на самом деле, и в какую сторону он влияет, я не анализировал, потому что задача эта громоздкая и в нашем контексте бессмысленная.
Второй вопрос возникнет у тех, кто разберется в программе. Она составлена "наоборот": мы не задаемся четными Ej, подбирая потом подходящие Pi, а последовательно перебираем Pi и получаем из него Ej простым умножением на 2. На самом деле, никакого вопроса здесь нет: нам достаточно построить кривые, а как - это безразлично. Нам даже все равно, четные ли числа Ej.
Если быть до конца честным, то получаемые с помощью выражения (4) значения прогрессии qi - это не самые минимальные из возможных значений минимальной доли точек рандеву, но они очень близки к минимальным, и ими вполне можно пользоваться для проверки достаточного условия существования точки рандеву. Причем, чем дальше по числовой оси, тем с большей надежностью. Убедиться в этом можно с помощью примера, который вычисляет реальные доли потенциальных точек рандеву для пар простых чисел, подозрительных на то, что они дают в сумме заданное четное число. Сохраните пример на диск и попробуйте поиграть с переменной ej, давая ей разные четные значения. Видно, что значения долей для этих пар образуют седло, и результат вычисления выражения (4) всегда находится в его нижней части. Извините за невнятность примера, но это - факультативная информация.
Кстати, вот еще один пример, позволяющий убедиться в справедливости нашего метода. Здесь сравнивается реальное количество пар простых слагаемых для каждого четного числа (Npairs) и прогнозируемое с помощью прогрессии (4) минимальное количество точек рандеву (qi*Ej). Совершенно однозначно видно, что после преодоления краевого эффекта ожидаемое минимальное количество точек рандеву надежно меньше, чем реальное число пар, но, конечно же, больше 1. То есть наша оценка действительно учитывает вариант "хуже худшего", но показывает, что всегда имеется не менее одной пары простых слагаемых.
Итак, мы убедились, что ej стремится к 0 гораздо быстрее, чем qi. Догадка Гольдбаха верна и подтверждена.
В заключение несколько замечаний.
Первое - философское. Обратите внимание НАСКОЛЬКО ej быстрее стремится к 0. И это, учтите, при наихудших условиях! Поэтому Догадка - это ОЧЕНЬ сильный закон. И, наверное, в этой силе - пролог ее мистической истории. Вот почему она буквально бросилась в глаза Гольдбаху (а если быть точным - то еще Декарту) во времена, когда простые числа не могли иметь никакого практического приложения и интересовали разве что чудаков. Вот почему люди искали, ищут и будут искать ясное и строгое ее доказательство: им просто не верится, что у такого сильного закона может его не оказаться. А с практической точки зрения эта сила проявляется в том, что пары простых слагаемых в подавляющем большинстве случаев обнаруживаются уже в непосредственной близости от половины четного числа. Убедитесь!
Второе - конкретное. Как видим, приведенное доказательство истинности Догадки нельзя считать абсолютно строгим. А возможно ли действительно строгое доказательство? Ответ: нет! Все те люди, включая дядю Петроса, кто потратил свои жизни на поиск доказательства, на самом деле строили вечный двигатель. Дело в природе простых чисел. Каждое очередное простое число ортогонально всем предыдущим и как бы создает новый мир, в котором не действуют законы предыдущих миров. И уж тем более для всех этих миров нет единого закона. Как невозможно точно выяснить, является ли взятое наугад нечетное число простым, не перебрав все предыдущие простые числа, так невозможно строго подтвердить Догадку.
Третье - эмоциональное. Печальное завершение романтической истории, правда? Так хотелось, чтобы у Догадки было такое же простое и красивое доказательство, как она сама: полтора десятка слов, и ничего не добавишь и не выкинешь... Вместо этого - кирпичи и приблизительные оценки. Жаль...
Сколько зарабатывают системные программисты?
| Date: | 17-29 ноября 1999 | |||||
| Newsgroup: | microsoft.public.win32.programmer.kernel | |||||
| Subject: | Salary question Вопрос о заработке |
|||||
| Участвуют: | - Alberto Orioli Wh Maxim S. Shatskih Jon Sturgeon Vladimir U. Gaikovich Vladimir Strogov Jamey kirby Oleksandr Bublyk Bob Moore Pavel A. - | nospam.batfish@mindspring.com a.orioli@aethra.it whehn@microtech.com.pl maxim@storagecraft.com JonS@futuresoft.nospam.com garry@infosec.ru svv@ultersys.ru jkirby@nospam.storagecraft.com sab@mastereye.kiev.ua bobm@mvps.org pavela@my-deja.com anhngo@my-deja.com | MindSpring Enterprises - TPNET MTU-Intel ISP Giganews.Com - Premium News Outsourcing - - - Anonymous Partisans Online MVP - - |
if(dhtml){document.write(" Все сообщения: [+][-] Открывать: [несколько]");}
[+] nospam.batfish@mindspring.comДействительно ли программисты, имеющие опыт программирования NT на уровне ядра, обычно зарабатывают больше, чем программисты только с опытом программирования для Win32? [+] Alberto OrioliЯ не знаю, но хотел бы знать, каковы заработки в разных странах для этого вида программирования. Я работаю в Италии, и в 1998 году мой доход составлял 17.0 евро/час. А как у вас, парни? [+] WhЯ из Польши. Я работаю не в Варшаве. Зарабатываю около 18 злотых в час (примерно 4 евро, включая налоги). В Варшаве программист зарабатывает на 50% больше. Программисты с опытом Win32 могут зарабатывать в Польше 12-30 злотых в час. [+] Maxim S. ShatskihА как евро соотносится к доллару? [+] Jon SturgeonКогда я смотрел последний раз, было $1.03 [+] Maxim S. ShatskihВ Москве хороший разработчик Win32 (COM/ATL/MFC) плюс SQL зарабатывает 800-1200 USD в месяц чистыми. (1200 - если вы лидер команды из ~10 человек). Получается примерно 4-6 евро в час. То есть то же самое, что в Польше. Программисты на уровне ядра получают больше - эта специальность гораздо более редкая, чем разработчики баз данных. [+] Vladimir U. GaikovichВы не правы насчет заработков в Москве (Россия). Не так уж много организаций в Москве, которым требуются (и которые готовы платить деньги) разработчикам на уровне ядра. И в Москве тоже... Поэтому программистам на уровне ядра платят хуже, чем хорошим программистам для Win32 - 500-800$ в месяц ($4-$5 в час) без налогов. [+] Vladimir StrogovЭто может быть верно только для компаний, которые нанимают системщиков для работы во внутрироссийских проектах на постоянной основе. Что касается проектов, для которых системные программисты нанимаются на контрактной основе, то в этом случае оплата может быть значительно выше (по моему личному опыту). В любом случае трудно найти таких разработчиков в любой стране. Если же говорить о разработчиках на уровне ядра, работающих в Москве для международного потребления, то там ситуация гораздо лучше. [+] Oleksandr BublykДа, та же самая ситуация на Украине. [+] Bob MooreОбычно да. Но мир требует не так уж много разработчиков на уровне ядра. [+] Jamey kirbyПрекрасно, давайте перенесем дисковые драйверы, файловые системы и управление памятью на прикладной уровень! [+] Pavel A.А это уже случилось в WinCE - все на прикладном уровне. [+] Jamey kirbyПростите, но я говорил о "настоящих" операционных системах. [+] anhngo@my-deja.comМне кажется, в США нет разницы, имеете ли вы опыт программирования на уровне ядра или в WIn32. Ну разве что системный программист имеет больше шансов найти работу. Но это совсем не означает, что вы будете зарабатывать больше. Обычно заработки различаются в разных штатах, так что я не могу говорить за всех. В Сан-Хосе, Калифорния, имея опыт работы 6-10 лет, можно зарабатывать $30-$60 в час. [+] Maxim S. ShatskihЭто до уплаты налогов? А как насчет других штатов? Там суммы меньше? Один мой друг уже около двух лет в США (Чикаго). Он работает на UNIX -подобной платформе и имеет ~100K$/год. Следует ли его считать гением (счастливчиком?) или это обычный случай? [+] Jamey kirbyВам недоплачивают или у вас не все в порядке. 10 лет опыта разработки драйверов среди моих знакомых оценивается намного выше, чем 100К в год. 150, может быть даже 180. То есть, пересчитывая на почасовую оплату, от 90 до 150 долларов в час. Конечно, это если вы действительно хороший программист. Я имею свой собственный бизнес, и я счастлив, если получается $10/час после выплат персоналу и оплаты расходов :) [+] anhngo@my-deja.comВы правы. Однако, существуют некоторые компенсации, которые следует учесть помимо прямого заработка. Не забывайте о регулярных доходах от консультаций, что совсем иное, нежели постоянная работа. Я согласен с суммой, которую вы называете. Но вот например консультанты делают гораздо больше денег. Если консультант имеет около 100К/год, это тоже недоплата. Минимальный доход консультанта представляется нормальным при 180К/год. Обычно, если работник получает 100К/год, он имеет реальную оплату на 30%-40% больше за счет социальных пособий, страховок и т.д. Добавьте также 10 дней праздников и 10 дней отпуска, это еще почти целый оплачиваемый месяц. Плюс 10 оплачиваемых дней на случай болезни. А также премии, доход от акций... Моя компания выплачивает 30% премиальных в год. Это означает, что человек, получающий около 100К/год, окончательно получит на 35% больше в смысле дохода, плюс еще 30К сверху (в зависимости от итогов работы компании). Он никогда не покинет компанию ради работы консультантом за 150К/год. Обеспечиваете ли вы другие доходы, кроме почасовых норм для ваших служащих? Многие из нас недополучают независимо от того, хороши мы или нет. У меня 7-летний опыта, так что я не могу сравнивать яблоко с яблоком, то есть с 10-летним опытом. Я тоже мечтаю стать консультантом, но не у каждого есть шанс на это. Между прочим, моя основная зарплата даже не приближается к 150K/год. Вы правы, возможно, мне недоплачивают, или я не очень хорош. Или я работаю в не очень правильной компании. Бизнес компании - это дисковые устройства, драйверы для них - не главный продукт. [+] Jamey kirbyДа, если работать консультантом полный день, то получится 180-200$/час.
Так в каком же веке мы живем?
Извини, усталый путник, случайно забредший на эту страницу. Предвижу твою кислую физиономию и предслышу добрые слова, которыми ты выражаешься в мой адрес в связи с нижеизложенным. В самом деле, просто достали все, от Папы Римского до изготовителей юбилейной туалетной бумаги, по 2000 метров в рулоне!
И еще: по домену millennium в каждой зоне (а за Россию радостно: ребята, прикинув процент не забывших латынь врачей и адвокатов на территории ru, не пожалели сотню баксов и на всякий случай зарегистрировали еще и millenium, ведущий на тот же хост. Уверен, большинство из набиравших url вручную попали на сайт именно этим путем. Сужу по себе. Наличие milennium и milenium не проверял, лень, но с них станется!)
И еще: кандидат в книгу рекордов Гиннеса в период с 15.06.99 по 31.12.99 съел 2000 куриных яиц. Скончался от легочной эмфиземы, осложненной сердечной недостаточностью, при прослушивании прощальной речи Первого Президента. А вот ел бы перепелиные, дурачок, мог бы скончаться куда более благородно, от сексуальных излишеств.
И еще: Windows 2000 с братом-Office'м. Точно говорю: прозорливец Билл прикидывал их раскрутку еще когда решал обозвать Chicago не 4.0, а 95.
И еще: умница Милен Фармер. И Милка с Охотного, берущая 20%-ю наценку, тоже большая молодец.
И еще: магическое число международных террористов на территории Чечни. При том, что примерно по столько их уничтожается ежемесячно, если судить по официальным сводкам. Число погибших наших, похоже, тоже неуклонно округляется до этого же значения.
И еще: хорошая передача Дм.Киселева "Национальный интерес-2000". Правда, в названии имеется неоднозначность: если это про зарплату, то в RU или в USD? Хотелось бы второе.
И еще, и еще, и еще...
Кажется, под верстовым столбиком с цифрами "2000" не пописала только соседская Аська, да и то, потому что она не собака, а кошка. Все остальные благополучно отметились, близко не подойдешь...
Но не могу молчать! Во-первых, cледующий случай высказаться по этой проблеме представится не скоро, боюсь, не доживу. И во-вторых, профессионально не терплю ошибок.
Всякому настоящему ассемблерщику знакомо это ощущение, когда программа готова к дистрибуции, но из личного опыта, из законов Мерфи, из знания жизни и еще черт знает откуда автор прикидывает, сколько в ней багов. Ненастоящие ассемблерщики и настоящие неассемблерщики могут примерно представить себе это ощущение, если когда-нибудь им доводилось летом, в жару, при мокрой спине, ехать домой в переполненном троллейбусе после посещения дешевой мужской парикмахерской. Возможно, именно в целях избежать упомянутых специфических ощущений, настоящие ассемблерщики почти не программируют и почти не стригутся.
Между тем, в ныне общепринятом определении того, в каком именно веке мы живем, имеет место самая настоящая ошибка.
Большинство людей, так уж сложилось, считает, что 2000-й год принадлежит 20-му веку. Одни - потому что с детства их учили считать: "раз, два, три, ...".
Настоящих ассемблерщиков чудаковатые родители учили, конечно же, так: "ноль, один, два,..., девять, а, бе, цэ, дэ, ..." и так далее, со всеми вытекшими отсюда печальными последствиями. Неспешно плыло светлое время шестидесятых, мир был наивен и добр. Печаталась цветная "Техника молодежи" про ЭВМ, про робота Сепульку, с фантастическим рассказом в каждом номере... А еще толстенькая "Наука и жизнь", и "Юный техник". За ночь глотался "Понедельник начинается в субботу". Полыхал дюзами "Непобедимый" Лема. Слово "голубой" ассоциировалось с небом. В году было 365 солнечных дней... Впрочем, это совсем другая история.
Вторые - потому что хотят стабильности. Третьи - потому что боятся будущего. Четвертым - наплевать. Пятые - решили, что так надо. Шестые - потому что верят пятым. Седьмые - потому что они посчитали, и у них так получилось. И так далее.
Очень интересно и привлекательно выглядит позиция официальных лиц, начиная от упомянутого Папы и кончая нашим начальником ЖЭУ. Не знаю, когда они все встречались, но на этой встрече они договорились о важном компромиссе. Чтобы не ссорить между собой настоящих ассемблерщиков и представителей всех остальных конфессий, принято было решение считать 2000-й год переходным, (не)принадлежащим обоим смежным векам. Слава мудрым!
Однако, время, как каждый вечер напоминает Михаил Леонтьев. Я лично вижу в его словах два смысла. Первый означает: "Треп закончен, переходим к делу". Второй обращает наше внимание на существо рассматриваемого вопроса.
Действительно, давайте вспомним, что речь идет о времени. И не просто о времени, а о времени, прошедшем от Рождества Христова. Потому что именно так называется Наша Эра (она же Новая Эра, она же Anno Domini). Вспомнили?
А теперь давайте считать. Время, оно ведь не кирпичи. Оно не поштучное, а непрерывное. Вот так его и будем считать.
Итак. Через пять минут после рождения Христа, когда Он лежал в яслях, освещаемый падавшим сквозь прореху в крыше убогого сарая светом Вифлеемской звезды, сколько Ему было от роду?
Правильно, ноль лет и пять минут.
А через месяц, когда, насытившись от груди Марии, радостно агукал, не зная о сужденных Ему Страстях?
Правильно, ноль лет и один месяц.
А через одиннадцать месяцев тридцать дней двадцать три часа и пятьдесят девять минут, когда Мария качала Его, спящего, в колыбели, мысленно поздравляя с первым годом жизни и мечтая о том, как Он вырастет добрым, хорошим сыном, будет счастлив и станет ей опорой в старости, как мечтают все родители от Начала Времен?
Правильно, ноль лет одиннадцать месяцев тридцать дней двадцать три часа и пятьдесят девять минут.
То есть нулевой год входит в жизнь Христа (впрочем, ничем не отличаясь от нулевого года жизни любого другого человека). Следовательно, нулевой год входит и в Нашу Эру, именно с него она начинается.
А в первом (не по счету, а по номеру) году Нашей Эры Христу уже исполнился год, и шел второй. Если этот тезис к данному моменту непонятен, то дальше можно не читать - читатель с автором в этой части общего языка уже не найдет. Это нормально.
Следовательно, первый век Нашей Эры составляли годы 00, 01, 02, 03 ... 99, поскольку в веке, по определению, 100 лет. А 100-й год относился уже, естественно, к следующему веку н.э.
Помаленьку экстраполируя, добираемся 1999 года. Кто не помнит, подсказываю: там был еще такой президент, Ельцин Борис Николаевич. Давно это было, еще в XX веке н.э. А вот 2000-й год - это уже XXI век, потому что за две тысячи лет понятие "век" ничуть не изменилось. Как было в первом веке 100 лет, с 00 по 99, так и осталось в ХХ-м c 1900 по 1999.
Делаем окончательный вывод. 2000-й год принадлежит XXI веку. Кто не понял: мы живем в новом, XXI веке н.э. Поздравим себя, дожили. А ведь могли и не дожить.
Здесь необходимо сделать маленькое пояснение. Все вышесказанное верно с формальной точки зрения. То есть если вы, допустим, настоящий ассемблерщик, и, как всякий настоящий ассемблерщик, ненавидите считать итерации в циклах while, for, do и until, потому что каждый раз нужно лезть в учебник, и все равно на 1 ошибешься, то с вашей точки зрения все сказанное - верно.
А вот если вы, допустим, раннесредневековый монах-богослов, и присутствуете в исторически поворотной точке пространства и времени, когда определяются все последующие воззрения на дату Рождества Христова, и только что вернулись со строительства монастырской стены, и собираетесь поработать с летописями, то все сказанное выше должно быть вам далеко не очевидно. Возьмете с устатку, да и назначите нулевой год жизни Христа первым. И пошло-покатилось. Вплоть до нашего 2000-го года, последнего года XX-го века.
Однако не в наших силах сейчас выяснить, откуда тысячу лет назад вернулся тот монах: с кирпичного мини-завода или из трапезной, где наоборот, созерцал ламинарный процесс перетекания анжуйского из одной емкости в другие. Поэтому давайте останемся на формальных позициях: мы - в XXI веке.
Через public mail мы получаем альтернативные мнения о рассматриваемом в этой статье вопросе. Так, Sergey Chehuta из WhiteTown прислал письмо:
|
Добрый день! Очень простое и наглядное дидактическое пособие для объяснения того, когда начинается новый век: "Второй ящик водки начинается с двадцать первой бутылки". :)) |
Ну и в заключение - маленькая сенсация. Основываясь на той же методике, порассуждаем не о годах, а о веках.
Первое тысячелетие составляли века: 00, 01, 02, 03, ..., 09.
Второе тысячелетие составляли века: 10, 11, 12, 13, ..., 19.
Еклмн! Братья и сестры, да мы уже сто лет, как живем в третьем тысячелетии! Во бараны, сколько денег в празднование вбухали, колеса по всему миру строили, хрустальные шары спускали, на одни фейерверки штуки три российских годовых бюджета спалили! Хорошо хоть мы, как обычно, умнее всех оказались: не стали транжириться, водкой и оливье обошлись.
Испугались? Шутка. Просто монах, который отвечал за века, точно вернулся с кирпичного завода. Ну не было в Нашей Эре Нулевого века. Его взяли, да и обозвали Первым. Видно, ненастоящими ассеблерщиками были те монахи...
P.S. Не забудьте разобрать елку.
Впечатления от "сегоднячко"
Хотелось бы поделиться впечатлениями от вчерашней (28.03.2000) передачи "Сегоднячко". Там Лев Юрьевич Новоженов вместе с милой Юлианной Шаховой и блистательным Игорем Воеводиным раскручивали народ на предмет поделиться: имел ли кто любовные акты в необычных местах. Не скрою, удивить удалось.
Во-первых, приятно поразили результаты телефонного опроса. Итоговых цифр не помню, но на каком-то этапе "да" было за 1000, а "нет" - меньше 100. Вот и гадай, то ли сограждане у нас такие романтичные, то ли понятие "необычное место" у них включает, допустим, кухонный стол.
Но если судить по звонкам, скорее первое, чем второе. Правда, откровенничали в основном мужики, а они, как известно, подвержены в таких рассказах древнему инстинкту рыболовов.
А вот одна симпатичная, судя по голосу, девушка выступила с рассказом о том, как со своим знакомым пришла на деловую встречу в Белорусское посольство, а там оказалась такая подходящая пожарная лестница.
А все говорят, что распад СССР повлек только негативные последствия! Ничего подобного: посмотрите, сколько новых посольств образовалось, сколько в них пожарных лестниц, и сколько симпатичных девушек имеют теперь возможность небанально ожидать назначенного времени для международной деловой встречи.
Запомнился еще рассказ одного джентльмена, с южным акцентом поведавшего о том, как при входе в самолет (с женой) ему как-то по-особому улыбнулась стюардесса. И в полете, пока жена наслаждалась сервисом первого класса, муж вышел в туалет и там у них с этой стюардессой все и случилось.
Дело происходило в 1998 году. Возможно, это была внучка той самой стюардессы, которая в свое время одолжила свой свитерок Эммануэль Арсан. Приятно, что годы идут, а сервис Аэрофлота по-прежнему на высоте.
Но несомненным победителем, конечно же, стал рассказ инженера-ракетчика. Оказывается, отечественные космические технологии предусматривают по двухчасовой готовности подъем на фермы обслуживания неких девушек, которые должны что-то там космическое пришивать нитками к чему-то тоже, само собой, космическому. А чтобы девушки пришили то, что надо, за ними наблюдают инженеры-ракетчики. (Именно так, во множественном числе, и те и другие в рассказе и упоминались.)
На старте стоял печально памятный проект "Фобос". Ласково дул теплый байконурский ветерок. Вокруг, насколько хватало взгляда, расстилалась бесконечная цветущая степь. Наверное, была весна - самое благодатное время в тех местах. Где-то внизу, метрах в пятидесяти, суетилась стартовая команда.
Девушки быстро справились с установленной циклограммой работой и занялись инженерами. Все участники этой истории до сих пор вспоминают ее как самое необычное романтическое приключение в своей жизни.
Только одно обстоятельство озадачивает их. Когда все закончилось и пора было спускаться, они всей компанией долго, сколько позволяло время, искали трусики одной из девушек, но так и не нашли. То ли их сдуло с фермы ветерком, то ли застряли где-то в конструкциях и потом сгорели в пламени двигателей, то ли завалились куда-то. Спускаясь в продуваемом лифте, девушка ежилась и прижимала юбку (допустим, они были в юбках, а не в комбинезонах) к бедрам руками. Все очень веселились.
Спустя несколько месяцев, при выполнении подлетного маневра к Марсу, связь с "Фобосом" была потеряна. Возможно, как раз в это время там, в холодной неуютной бездне, над неприветливым красным диском, сервопривод антенны честно, добросовестно и безуспешно пытался преодолеть сопротивление изящно обшитой кружевом резинки. Привод визжал и визжал, падал заряд аккумуляторов, но антенна так и не двинулась с места. Через несколько часов все было кончено. Фиолетовое небо Марса неторопливо прочертила блеклая звездочка. Еще двадцать минут - и на красное ложе марсианской пустыни опустился обгорелый кусочек кружевной ткани. Вечный ледяной ветер повлек его по мертвым барханам, загнал в какую-то лощину и быстро занес сыпучим песком.
Во всей этой истории есть две проблемы, которые необходимо обдумать.
Во-первых, при сходных обстоятельствах погиб и второй "Фобос". Неужели наши специалисты по космической технике столь беспечны, что умудрились дважды потерять трусики?
И второе: неужели технология подготовки к пуску Mars Polar Lander тоже предусматривала пришивание чего-то нитками?
Статьи по Assembler
Как изменить способности компьютера
Автор: Бордачев Андрей Юльевич (glareboa@mail.ru)
С другими интересами автора вы можете познакомиться здесь.
Статья была впервые опубликована в журнале Библиотека информационной технологии (БИТ), N 10 за 1994 год.
Огромное количество микросхем от "простеньких", выполняющих логические операции типа И,ИЛИ,НЕ и др., до сверхинтегрированных контроллеров в которые "впихнута" чуть ли не вся машина (ПЭВМ). Всевозможные дополнительные платы (адаптеры, контроллеры), винчестеры и т.д. Все это призвано к жизни и к взаимодействию с целью облегчить существование своим хозяевам (пользователям, программистам).
Что же, картина получается непростая. Но говорить о сложности устройства компьютера, о сложности его частей, о сложности процессов их взаимодействия излишне - это и так понятно.
Если Вы дочитали до этого места то, может быть, подумали: а к чему это автор намекает на всякие сложности, очевидные же вещи. Если появился вопрос - дадим на него ответ. Любой человек, имевший возможность работать с компьютером, и пользователь, и программист, в один прекрасный день думает: а хорошо бы, если бы компьютер ("ящик") умел делать еще и то-то, и так-то. Установить бы дополнительную "платочку" в компьютер и получить желаемое.
"Ну,"- скажет критически настроенный читатель, - "это же надо собирать какие-то контроллеры, разбираться в микросхемах, понимать в управляющих диаграммах, то есть быть, как минимум, схемотехником."
Он прав, но не совсем. Конечно хорошо быть схемотехником (автор по профессии схемотехник и утверждает это обоснованно), но не обязательно, а вот программистом, для того чтобы применить опыт, описываемый далее, быть нужно.
Дело в том, что многие сетевые адаптеры имеют гнездо для установки ПЗУ (постоянного запоминающего устройства, rom, read only memory), позволяющего при наличии локальной сети осуществлять удаленную загрузку операционной системы. Но, как показала практика, в большинстве случаев эта возможность не используется. Если в это гнездо вместо микросхемы со старой ПЗУ (если она там вообще есть) установить новую, со своей программой, то компьютер обретет новые дополнительные свойства. Например, это может быть программа, защищающая компьютер от несанкционированного доступа, или антивирусная защита (монитор, ревизор и т.п.). Так что учиться схемотехнике не придется. Можно сразу приступить к разработке программы для ПЗУ.
Но в чем же здесь преимущество? Одни только сложности. Вынимай микросхему, ищи новою. Программу нужно еще как-то поместить в ПЗУ. Да и вообще, будет ли она выполняться? Кто или что сделает так, чтобы эта программа получила управление?
Вот именно об этом "механизме", заложенном в BIOS и обеспечивающем возможность выполнения программ, "прошитых" (так называют программы, записанные в ПЗУ с помощью программаторов) в ПЗУ, а также о том, каким образом необходимо оформлять эту программу, и хотелось рассказать заинтересованным читателям.
Отметим при этом, что преимущество программы, размещенной в ПЗУ, заключается в том, что она неуничтожаема и отработает еще задолго до того, как начнут свою работу по загрузке операционной системы с диска модули MAIN BOOT и BOOT. И хотя эти части системы наиболее часто подвержены атакам вирусов, мы хозяева положения и можем блокировать любую атаку или же осуществить восстановительные работы.
волшебная возможность bios
Каким же образом машина узнает, что ей необходимо выполнить еще одну дополнительную программу? Задавшись вопросом, начинаем искать ответ. К счастью есть палочка-выручалочка - листинг BIOS, поставляемый в комплекте (за отдельную плату) с "персоналками". В этом листинге содержится информация, раскрывающая тайны функционирования машины. В частности, и ответ на наш вопрос.
Оказывается, что среди прочих важных дел, совершаемых подпрограммами BIOS, есть дело, заключающееся в проверке присутствия ПЗУ в адресном пространстве с С800:0 до E000:0.
Это пространство сканируется с шагом в 2048 байт на предмет наличия "подписи" 0AA55H. Но ведь может быть и совпадение. Чтобы не ошибиться, после того, как найдена "подпись", производится расчет контрольной суммы методом сложения по модулю 100H (сложение побайтно без учета переноса). Результат при этом должен получиться равным нулю. Но сколько же байтов необходимо просуммировать? Эта информация содержится в одном байте, следующем за подписью. Причем чтобы узнать размер ПЗУ в байтах, необходимо умножить это число на 200H.
Если все эти условия выполняются, BIOS считает, что перед ним ПЗУ, оформленное по правилам, и совершает последний шаг - передает управление на смещение +3 (отсчет начинается с нуля) относительно найденного сегмента. Следовательно, там должна располагаться первая выполняемая команда нашей программы. Обычно это команда jmp. Совершив все необходимое, наша программа должна вернуть управление обратно в BIOS с тем, чтобы та продолжила поиск аналогичных ПЗУ. Следовательно, наша программа должна заканчиваться командой retf.
Для того, чтобы было более ясно, о чем шла речь, ниже приводятся фрагменты кода BIOS, реализующие необходимые проверки и передачу управления.
;#################################################################### ; Поиск дополнительной ROM в области памяти C800->E000 в блоках по 2K ; Модуль ROM должен иметь подпись '55AA' в первых двух байтах и ; индикатор размера ((размер в байтах)/512) в третьем байте. ; Исполняемый код должен начинаться с четвертого байта. ;//////////////////////////////////////////////////////////////////// ROM_SCAN: ;начало фрагмента кода BIOS STI ;разрешить прерывания MOV DX,0C800H ;начало сканирования ROM_SCAN2: MOV DS,DX ;ds - сегмент очередного ROM-модуля SUB BX,BX ;bx - указатель в ROM-модуле MOV AX,[BX] ;взять первое слово в модуле CMP AX,0AA55H ;подпись найдена? JNZ NEXT_ROM ; нет - перейти к следующему базовому адресу ROM CALL ROM_CHECK ; да - проверить контрольную сумму JMP ARE_WE_DONE ;перейти к проверке конца области дополнительной ROM NEXT_ROM: ;следующий ROM-модуль ADD DX,0080H ; позиционировать новый модуль через 2K байт (как сегмент) ARE_WE_DONE: ;проверка конца области дополнительной ROM CMP DX,0E000H ;достигнут адрес E0000? JL ROM_SCAN2 ; нет - перейти к новой проверке подписи ;//////////////////////////////////////////////////////////////////// ;Последующий код не относится к теме статьи ;... ;... ;... ;#################################################################### ;Эта подпрограмма вычисляет контрольную сумму дополнительных ;модулей ROM и, если она верна, вызывает программу из модуля ;(При входе регистр ES указывает на сегмент данных BIOS) ;//////////////////////////////////////////////////////////////////// ROM_CHECK PROC NEAR ;вычислить число байт для сканирования SUB AH,AH ;ax - индикатор размера модуля MOV AL,[BX+2] MOV CL,09H ;ax=ax*512 SHL AX,CL MOV CX,AX ;cx - счетчик байт для сканирования ;вычислить указатель на следующий модуль и поместить его в dx PUSH CX MOV CX,4 SHR AX,CL ADD DX,AX POP CX ;вычислить контрольную сумму модуля CALL ROM_CHECKSUM JZ ROM_CHECK_1 CALL ROM_ERR ;контрольная сумма на совпала! JMP ROM_CHECK_END ;---- Вызов программы, находящейся в ПЗУ ---------------- ROM_CHECK_1: PUSH DX ;сохранить текущее значение указателя MOV ES:IO_ROM_INIT,0003H ;подготовить смещение MOV ES:IO_ROM_SEG,DS ;подготовить сегмент CALL DWORD PTR ES:IO_ROM_INIT ;вызвать программу POP DX ;восстановить указатель ;---------------------------------------------------------- ROM_CHECK_END: RET ;завершить работу ROM_CHECK ENDP ;=========================================== ;Подпрограмма вычисления контрольной суммы ;=========================================== ROM_CHECKSUM PROC NEAR XOR AL,AL NEXT_BYTE: ADD AL,[BX] INC BX LOOP NEXT_BYTE OR AL,AL ;сумма равна нулю? RET ROM_CHECKSUM ENDP
Осталось обговорить еще одну тонкость. Каким образом обеспечить равенство нулю контрольной суммы? Ведь очевидно, что само собой это условие не будет выполняться. Идея проста - необходимо дополнить получившуюся сумму до нуля. Делается это размещением числа, равного разнице между 100H и получившейся контрольной суммой в байте (перед расчетом он должен быть равен нулю), следующем за последней командой программы. Отметим также, что расчет контрольной суммы ведется по количеству байт, равных размеру ПЗУ, а не по размеру получившегося кода программы.
После сказанного можно отобразить структуру программы следующим образом:
| смещение | код | комментарий |
| +00 | db 55h,0AAh | подпись |
| +02 | db (?) | индикатор размера модуля |
| +03 | db 0EAh,(?),(?) | передача управления коду (metka) |
| +06 | ;... | константы программы |
| +?? | metka: ;... |
код программы |
| +?? | retf | возврат управления BIOS |
| +?? | db (?) | дополнение контрольной суммы до 100h |
Если ваша программа должна работать только на этапе старта компьютера, то ее переменные вы можете размещать в любой области conventional ОЗУ выше той, которая отведена под переменные BIOS (то есть в диапазоне сегментных адресов 0050:0000...9000:FFFF) - на этом этапе указанный диапазон свободен. Гораздо сложнее ситуация, когда ваша программа должна работать после того, как операционная система загружена: например, обслуживать прерывания или предоставлять свои процедуры для вызова обычными программами. В этом случае вам придется предусмотреть механизм, исключающий конфликты использования памяти.
В заключение - две программы.
;############################################################ ;Пример программы рассчитывающей контрольную сумму, ;вычисляющей дополняющий байт и формирующей обрабатываемую ;программу в виде файла на диске. ;============================================================ TITLE ComputeCRC INCLUDE MACRO.DEF LENGTHROM EQU 2000H ;Размер ПЗУ в байтах (8192) CODE SEGMENT BYTE PUBLIC ASSUME CS:CODE,DS:CODE ORG 100H START: JMP SHORT BEGIN ;Имя обрабатываемой программы FileName: DB 'FileName.Ext',0 BEGIN: ;Открыть файл для чтения MOV AX,3D02H MOV DX,OFFSET FileName INT 21H JNC M1 JMP ER M1: ;Прочитать файл в конец программы MOV BX,AX MOV AX,3F00H MOV CX,LENGTHROM MOV DX,OFFSET LastByte INT 21H JNC M2 JMP ER M2: ;Запомнить количество прочитанных байт MOV CX,AX ;Установить указатель позиции в файле на начало MOV AX,4200H PUSHR CX,DX ;PUSHR,POPR - макросы описаны в MACRO.DEF MOV CX,0 ;см.статью Макросы First и Second
MOV DX,0 INT 21H POPR CX,DX JC ER PUSHR AX,BX,CX,DX,DI ; Рассчитать контрольную сумму PUSHR CX XOR AX,AX MOV BX,OFFSET LastByte M3: ADD AL,[BX] INC BX LOOP M3 ;Вычислить дополняющий байт MOV CX,100H SUB CX,AX MOV AX,CX POPR CX ;Настроить указатель на начало обрабатываемой программы PUSHR AX MOV DI,CX ADD DI,OFFSET LastByte-1 ;Найти место в обрабатываемой программе для записи вычисленного байта MOV AX,0 STD REPNE SCASB INC DI POPR AX ;Записать его туда MOV [DI],AL POPR AX,BX,CX,DX,DI ;Записать получившийся модуль на диск MOV AX,4000H INT 21H ;Ошибки не обрабатываем т.к. программа проста ER: ;Закрыть файл MOV AX,3E00H INT 21H ;Нормальное завершение программы MOV AX,4C00H INT 21H LastByte: CODE ENDS END START
В качестве примера, реализующего структуру, описанную выше, приведена программа-скелет, способная стать основой для написания ваших программ:
;############################################################ ;Пример "скелета" программы для записи в ПЗУ ;============================================================ TITLE BiosPassword LENGTHROM EQU 2000H ;Размер ПЗУ в байтах (8192) CODE SEGMENT BYTE PUBLIC ASSUME CS:CODE,DS:CODE ORG 0 START: DB 55h DB 0AAh ;Размер ПЗУ по модулю 200H DB LENGTHROM SHR 9 ;Первая выполняемая команда JMP BEGIN ;--------------------------------- Данные ---- CP1 DB 14,'Copyright (C) ' CP2 DB 18,'by Bordachev A.Y. ' CP3 DB 18,'ver. 1.00-93/03/31' UNFACE DB 4,':-( ' FACE DB 4,';-) ' BIOS DB 5,'BIOS ' PROMPT DB 9,'PASSWORD:' ERR1 DB 4,'ERR1' ;----------------------- Начало программы ---- BEGIN: ;Не забудьте настроить и другие регистры если необходимо MOV AX,CS MOV DS,AX ;--------------------------------------------- ;- Здесь располагается код Вашей программы. - ;--------------------------------------------- ;Вернуть управление вызвавшей программе (BIOS) RETF ;Сюда запишем дополняющий байт DB (0) CodeEnd: ;Все что ниже, заполним кодом 0FFH ;так как в чистом ПЗУ обычно содержится ;именно этот код. DB (LENGTHROM-(OFFSET CodeEnd-OFFSET START)) DUP (0FFH) LastByte: CODE ENDS END START
Читайте также другие статьи Андрея Бордачева: О формате PCX и Макросы First и Second.
Макросы first и second
Автор: Бордачев Андрей Юльевич (glareboa@mail.ru)
С другими интересами автора вы можете познакомиться здесь.
Комментарии: assembler.ru
Макросы - это мощный, удобный и гибкий инструмент. По сути дела, это целый язык программирования, имеющий единственную цель - совершенствование языка ассемблера.
И он прекрасно справляется с этой задачей. Возможны и существуют технические решения, построенные исключительно на макросредствах, превращающие язык ассемблера в полноценный структурированный язык (то есть содержащий полностью функциональный набор структурных операторов if-else-endif, while-next, do-while, continue, break и т.д.) или даже в некоторое довольно близкое подобие объектно-ориентированного языка. При этом все достоинства ассемблерного программирования сохраняются.
Здесь приведены два варианта макросов, решающих одну и ту же задачу - групповую загрузку в стек и восстановление из стека регистров и переменных. Основная особенность этих макросов - сохранение очередности упоминания параметров в загружающем и восстанавливающем вызовах. Например:
pushr eax,ecx,edi,vasya,edx,petya;...popr eax,ecx,edi,vasya,edx,petya
Это очень удобно. Во-первых, снижается вероятность ошибок из-за перепутывания очередности загрузки/восстановления параметров. Во-вторых, облегчается набор текста: достаточно скопировать строку и заменить в ней команду pushr на popr. В-третьих, несколько сокращается длина листинга, о чем тайно мечтают, но боятся признаться все ассемблерщики.
Эту же задачу решает пара макросов @push и @pop, описанных в файле @struct.inc для проекта MyCall.
Такое разнообразие демонстрирует нам гибкость макроязыка и гамму возможных приемов, которые он дает.
;#################################################################### ;Вариант FIRST
;//////////////////////////////////////////////////////////////////// ;Макрос pushr
;В процессе макрорасширения (встраивания макроса в тело программы ;во время ее компиляции) проверяется, сколько параметров из 8 ;максимально возможных указал программист, и формируется столько ;последовательных команд push, сколько нужно. Очевидно, что ;максимальное число параметров макроса без труда может быть увеличено ;настолько, насколько нужно. ;==================================================================== pushr MACRO R1,R2,R3,R4,R5,R6,R7,R8 IFNB
;Работает так же, как и pushr, за одним исключением: очередность проверки ;обратная. Это позволяет программисту не менять очередности упоминания ;параметров при использовании макроса в программе. ;==================================================================== popr MACRO R1,R2,R3,R4,R5,R6,R7,R8 IFNB
;//////////////////////////////////////////////////////////////////// ;Вспомогательные макросы
;==================================================================== makevar MACRO name,ext,val ;формирует имя операнда &name&ext EQU
ENDM
makevarn MACRO name,ext ;формирует команду push push &name&ext ENDM
makevarm MACRO name,ext ;формирует команду pop pop &name&ext ENDM ;==================================================================== ;Макрос pushr
;В отличие от варианта First, макрос производит опрос параметров в цикле, ;построенном на директиве IRP. Благодаря этому больше не существует ;ограничения на число параметров. ;==================================================================== pushr MACRO args ;синтаксис указания параметров:
IRP s,&args fcnt=fcnt+1 makevarn t1,%fcnt ENDM ENDM ;==================================================================== ;Макрос popr
;==================================================================== popr MACRO args ;синтаксис указания параметров:
makevarm t2,%fcnt fcnt=fcnt-1 ;знак "-" обеспечивает обратный порядок следования команд! ENDM ENDM
Эта статья не ставит задач изучения макроязыка. Если вы хотите узнать, почему в варианте Second параметры надо брать в угловые скобки, что означает амперсанд в записи &args и знак процента в записи %fcnt, каковы возможности директив IFNB и IRP,- воспользуйтесь документацией на ваш компилятор, и немножко поэкспериментируйте. Будьте уверены: ваши усилия многократно окупятся повышением производительности труда.
Читайте также другие статьи Андрея Бордачева: О формате PCX и Как изменить способности компьютера.
обсуждение статьи
Нам прислал письмо Argus (Мельников Василий Юрьевич, vasily@geolog.kmv.ru, http://www.halyava.ru/club477), в котором справедливо напоминает, что макросы в макроассемблере обладают замечательным свойством рекурсии, позволяющим создавать компактные и изящные программные конструкции. В частности, обсуждаемые в этой статье макросы pushr и popr в рекурсивной версии, предложенной Argus'ом, будут выглядеть так:
;#################################################################### ;Вариант THIRD
;//////////////////////////////////////////////////////////////////// ;Макрос pushr
;Рекурсия обеспечивается тем, что в теле макроса pushr присутствует ;вызов этого же макроса, но без первого аргумента. Легко видеть, что ;рекурсивные итерации макрорасширения будут происходить до тех пор, ;пока не исчерпается весь действительный список аргументов. ;==================================================================== pushr MACRO R1,R2,R3,R4,R5,R6,R7,R8 IFNB
;Видно, что этот макрос также рекурсивный, но количество итераций ;макрорасширения определяется в нем не числом фактических аргументов, ;как в pushr, а числом формальных параметров (здесь - 8), пока все ;они, сдвигаясь вправо, не заменятся заглушкой dummy. При этом ;реальный код команд pop будет сформирован только для параметров, ;которым соответствуют действительные фактические аргументы. ;==================================================================== popr MACRO R1,R2,R3,R4,R5,R6,R7,R8 IFNB
Рекомендуем обратить внимание на суть заглушки dummy. Это - не константа и не переменная, она не требует объявления или описания. Здесь мы наблюдаем прием, характерный в ассемблере исключительно для макросов, поскольку они способны работать с литералами (текстовыми фрагментами), а dummy здесь - именно литерал. (Естественно, вместо слова dummy можно применить любую другую комбинацию символов). Он используется в формировании макровызовов, а не макрорасширений, то есть участвует в формировании исходного текста программы, а не исполняемого кода.
О формате pcx
Автор: Бордачев Андрей Юльевич (glareboa@mail.ru)
С другими интересами автора вы можете познакомиться здесь.
Статья была впервые опубликована в журнале Библиотека информационной технологии (БИТ), N 8 за 1993 год.
Статья сопровождается примером - исходным текстом программы opcx.zip (3588 байт), выполняющей обработку pcx-файла.
Коротко о том, почему возникла эта статья.
О формате файлов PCX написано довольно много. Это совершенно различная по своему характеру и содержанию литература. Краткие описания формата, подробнейшее изложение способов работы с огромным количеством всеохватывающих функций в фирменных пакетах, библиотеках и многое, многое другое. Вся эта немалая по объему информация хранится на тот случай, когда может возникнуть необходимость создать что-нибудь красочное и впечатляющее для глаз пользователя, желающего украсить экран своего дисплея изображениями, схожими по красоте и точности воспроизведения с фотографическим отпечатком.
Надо сказать, что в работе системного программиста такие задачи практически не возникают. Но вот наступил момент... Перечитав всю информацию, наличествавшую в тот момент в личном архиве, стало понятно, что есть готовность задавать вопросы по теме, но отсутствует готовность осуществить поставленную задачу. Связано это было с тем, что с одной стороны, в разных источниках приводилась противоречащая друг другу информация, а с другой - нигде не обсуждались вопросы возможных непринципиальных, но важных с точки зрения дешифрации отличий формирования данных в файле PCX. И поэтому, как и большинству программистов нашего времени, пришлось провести многочисленные эксперименты и приобрести недостающую информацию и опыт. В результате были написаны процедуры обработки файлов PCX на ассемблере, позволившие создать компактную, быструю, легко модифицируемую программу вывода изображения, исходный текст которой будет приведен в конце статьи.
Цель данной статьи - дать достаточное количество информации для того, чтобы, приступая к написанию программ обработки PCX файлов, не испытывать сомнений в принципиальных вопросах.
формат pcx
Не претендуя на полноту описания форматов, обрисуем общую структуру файла, структуру заголовка файла, поля заголовка файла, способ упаковки данных и некоторые тонкости обработки данных.
Формат PCX постоянно совершенствуется, а также в зависимости от программного продукта может быть несколько модифицирован.
Общую структуру файла можно условно разбить на две части: заголовок файла и упакованные данные (рис.1).
В новых, 256-цветных форматах, присутствует третья часть.
| раздел файла | размер, байт |
| заголовок файла | 128 |
| упакованные данные | размер файла-128 |
| дополнительные данные для 256-цветных режимов | 769 |
Заголовок файла - это набор структурированных полей фиксированной длины. На рисунке 2 представлена схема заголовка файла:
| N | смещение | название | размер | дополнительная информация | |
| 01 | 00 | 00h | manuf | byte | Изготовитель |
| 02 | 01 | 01h | hard | byte | Информация о версии |
| 03 | 02 | 02h | encod | byte | Способ кодирования |
| 04 | 03 | 03h | bitpx | byte | Бит на точку |
| 05 | 04 | 04h | x1 | 2 bytes | Размеры образа |
| 06 | 06 | 06h | y1 | 2 bytes | |
| 07 | 08 | 08h | x2 | 2 bytes | |
| 08 | 10 | 0Ah | y2 | 2 bytes | |
| 09 | 12 | 0Ch | hres | 2 bytes | Разрешение дисплея по горизонтали |
| 10 | 14 | 0Eh | vres | 2 bytes | Разрешение дисплея по вертикали |
| 11 | 16 | 0Fh | clrma | 48 bytes | Палитра |
| 12 | 64 | 40h | vmode | byte | Видео режим |
| 13 | 65 | 41h | nplanes | byte | Количество слоев |
| 14 | 66 | 42h | bplin | 2 bytes | Байтов на строку |
| 15 | 68 | 44h | palinfo | 2 bytes | Тип палитры |
| 16 | 70 | 46h | shres | 2 bytes | Разрешение сканера по горизонтали |
| 17 | 72 | 48h | svres | 2 bytes | Разрешение сканера по вертикали верт |
| 18 | 74 | 4Ah | xtra | 54 bytes | Обычно не используется |
В первой графе указывается порядковый номер поля, который приведен для более легкой ориентации в таблице и более легкого изложения материала. Во второй - смещение до начала поля в десятичном и шестнадцатиричном исчислении. Третья содержит название, которое сохранено таким же, как и в пакете PCX Programmer's Toolkit фирмы Genus Microprogramming.
Рассмотрим подробнее каждое из полей.
Поле 01 (manuf) содержит некоторое число (например 10 для "PC Paintbrush" Zsoft Corp.), говорящее о том, в каком из редакторов создан этот PCX-файл.
Поле 02 (hard) - информация о версии формата.
Поле 03 (encod) позволяет определить, изменился или нет способ сжатия данных. До сих пор большинство редакторов заполняет это поле значением равным 01, свидетельствующим о том, что используется метод кодирования длинными сериями или метод группового кодирования (см. описание ниже).
Эти три поля, как очевидно, не нужны непосредственно при дешифрации данных, но при возникновении проблем с расшифровкой позволят сориентироваться программисту в том, как осуществлять дешифрацию, если есть достаточно подробное описание форматов, или к какой фирме обращаться за разъяснениями.
Поле 04 (bitpx) показывает, сколько бит отводится для сохранения информации о яркости пикселя в слое. Является достаточно удобным признаком определения (в дополнение к другим признакам) содержит ли данный файл 256-цветную палитру. При значении равном 08 - содержит.
Пиксел - минимальный графический объект. Обычно соответствует точке на экране, что в общем случае необязательно.
Слой (plan) - совокупность байтов видео-памяти. Количество слоев зависит от номера установленного видеорежима и, естественно, от физической организации видеопамяти.
Поля 05...08 (x1, y1, x2, y2) определяют геометрические размеры картинки в пикселах. Например, если размер картинки 640 пикселов по горизонтали и 350 по вертикали, то в этих полях будут записаны следующие числа: 0, 0, 639, 349.
Поля 09,10 (hres, vres) позволят узнать разрешающую способность дисплея, при "участии" которого создавалась та или иная картинка.
Поле 11 (clrma) хранит информацию о палитре картинки. Данные хранятся 16-ю триплетами, определяющими значение каждого из 16 регистров палитры:

Рис.3. Формат поля номер 11
Для 256-цветных режимов количество байтов, необходимое для хранения всей палитры, равно 768, и понятно, что в поле 11 не хватает места для размещения этого массива. Поэтому эти триплеты располагаются в конце файла после байта-"маяка", равного 0Ch:

Рис.4. Расположение данных 256 - цветной палитры
Для того, чтобы попасть на это поле, необходимо:
Делать это, конечно, необходимо в том случае, если вы уверены, что pcx-файл содержит 256-цветную палитру.
Байты RGB поля 11 могут принимать значения в диапазоне от 0 до 255. Но не каждый адаптер дисплея сможет воспроизвести заданное значение. Например, для EGA адаптеров диапазон этих значений лежит в пределах от 0 до 63. Поэтому для правильной интерпретации палитры необходимо учитывать характеристики оборудования, на котором будут воспроизводиться изображения.
Прим.ред.: Не забывайте, что статья была написана в начале 90-х годов. В настоящее время адаптер EGA - в далеком прошлом, и практически весь парк современных компьютеров общего применения свободно воспроизводит 256-цветную палитру.
Кроме того, в настоящее время появились программные продукты (например, PCX Programmer's Toolkit фирмы Genus Microprogramming), в которых значения палитры "нормализованы", т.е. лежат в диапазоне от 0 до 63, и значащими являются только младшие 6 битов каждого байта.
Способ декодирования данных поля 11 в байт для инициализации регистра палитры прост, и потому отошлем читателя к исходному тексту процедуры CPALEGA (см.ниже). Заметим только, что для адаптера VGA имеет смысл использовать другой способ установки необходимой палитры, связанный с инициализацией DAC-регистров.
Поле 12 (vmode) подскажет, какой видеорежим необходимо установить.
Поле 13 (nplanes) позволит определить, сколько слоев видеопамяти в этом режиме, и алгоритм вывода данных на экран.
Подразумевается, что возникает необходимость отслеживания ситуации заполнения строки и переключения вывода на другой слой.
Поле 14(bplin) определяет количество байтов на строку внутри слоя. Под строкой подразумевается последовательность байтов, содержащих графические данные, из которых строится изображение.
Поле 15 (palinfo) интерпретирует палитру. Если байт равен 01, то палитра цветная, если 00 - то передаются градации серого.
Поля 16,17,18 (shres, svres, xtra) могут содержать различного рода дополнительную информацию, которая сильно специфична и, как правило, не нужна для простых программ, выводящих рекламные картинки. Обычно эти поля заполняются нулями.
Как было уже сказано, при упаковке данных изображения используется метод, носящий название "кодирование длинных серий". Суть метода довольно проста. Каждой повторяющейся последовательности байтов можно поставить в соответствие код, состоящий из двух байт. Первый из них является счетчиком повторений байта, стоящего вторым в этом коде. Например, если есть последовательность из 34 (22h) байтов типа 55h, то вместо этих 34(22h) байтов в закодированном файле будут стоять два числа: 22h,55h:

Рис.5. Кодирование длинных серий
В файлах PCX возникает необходимость различать байт-счетчик и байт-эталон. Это связано с тем, что отдельные части изображения могут состоять из неповторяющихся данных и байт-счетчик в этом случае отсутствует. Поэтому он должен быть каким-либо способом помечен. Это можно сделать, установив два старших бита в 01, а оставшиеся 6 битов будут хранить значение счетчика. Следовательно в файле PCX та же последовательность из 34(22h) байтов будет представлена как 0E2h,55h.
Очевиден и способ декодирования данных:
Отметим, что в случае, когда байт-эталон имеет установленные стаpшие биты в единицу (напpимеp, чеpточка длиной в 8 пикселей и следом за ней 8 чеpных пикселей пpедставляются в виде двух байтов 0FFh,00h), то из-за этого он похож на байт-счетчик, и его пpедваpяют байтом-повтоpителем, pавным единице. Этим снимается неоднозначность опpеделения типа данных.
Теперь, для завершения обсуждения вопроса о кодировании, покажем, каким образом располагаются в файле данные, хранящие изображение.
Вначале идут байты, относящиеся к нулевому слою, затем - к первому, и так далее до четвертого включительно, после чего все повторяется сначала:

Рис.6. Порядок следования данных
программа обработки файла pcx
В общем случае программы должны обладать возможностью и создавать, и интерпретировать файл этого формата.
Под интерпретацией здесь понимается возможность правильно понимать данные, хранящиеся в заголовке pcx-файлов, и в соответствии с ними (данными заголовка) строить на экране изображение.
Как правило - это мощные графические редакторы или утилиты, им сопутствующие. Но есть задачи, не требующие создания файлов формата PCX. Необходимо только второе - интерпретировать. Как правило, это универсальные программы-вьюеры. Есть задачи и более простые - показать на экране рекламное изображение. Для этого надо совсем мало, так как в связи с неизменностью условий работы программы практически отпадает необходимость подробно анализировать заголовок файла PCX. Такая задача и предлагается вашему вниманию.
Наилучший способ объяснить - это привести пример. Поэтому приступим к изложению задачи, которую необходимо решить и к описанию программы, ее реализующей.
Построение любой программы начинается с четкого уяснения условий, поставленных задачей. Каковы же эти условия?
Первые два условия объясняются желанием не увеличивать "безгранично" размеры программы*), к которой пристыковывается этот модуль, и, во-вторых, быть независимым от размеров pcx-файла и от типа (.com или .exe) основной программы. Предваряя результат, скажем, что размер исполняемого модуля менее одного килобайта. А процедуры, относящиеся непосредственно к декодированию и выводу изображения, занимают около 600 байт.
*) Это ухудшило бы потребительские характеристики программы. Например, если вы хотите использовать какой-либо популярный драйвер или утилиту (с разрешения автора) для рекламы то было бы совершенно неприемлемо увеличивать размер программы на 10 или больше килобайт.
Это объясняется тем,что программа написана на языке ассемблера, и тем, что поставленная задача проста, и поэтому количество процедур мало. Это же обеспечивает выполнение и третьего условия.
Под способностью "понимать" имелась ввиду реализация программного блока, анализирующего необходимые поля заголовка pcx-файла, и, в зависимости от их содержимого, формирующего команды, позволяющие программе ветвиться.
Выполнение пятого условия необходимо для того, чтобы обеспечить некоторую свободу маневра и запас "прочности" при "встрече" с различной техникой с одной стороны, а с другой - возможности более полно использовать палитру.
Говоря о шестом условии, подчеркнем, что существует большое разнообразие нюансов при записи данных в pcx-файл.
Например, автор столкнулся с тем, что достаточно часто графические редакторы или утилиты формируют pcx-файл, считая некоторые характеристики изображения неизменными (например, размер текстовой строки считается величиной постоянной и равной 80 символам в графическом редакторе PMTV, поставляемом совместно с ручным сканером "A4SCAN" фирмы A4TECH CO.ltd.(U.S.A.)).
Это дает определенные преимущества авторам вышеупомянутого программного обеспечения при его создании, так как сокращается контроль разнообразных условий, и по той же причине упрощается работа непосредственно в графическом редакторе.
Поясним это утверждение следующим примером.
Предположим, мы нарисовали линию белого цвета длиной в 35 пикселей. Размер же всего изображения примем равным 67x1 пикселей, для упрощения рисунков и рассуждений:

Рис.7. Структура линии на экране
Данные о 8 точках умещаются в одном байте. Подсчитав количество байтов, видим, что нужно 5 байтов для хранения информации о всех точках линии одного слоя. Причем в пятом байте используются только три бита. Для хранения же информации о всех точках изображения необходимо [67/8]·4=9·4=36 байтов (здесь квадратные скобки означают операцию округления в большую сторону).
Посмотрим теперь, как выглядит закодированное изображение в pcx-файле, формируемом редакторами PC Paintbrush и PMTV A4TECH (рис.8).
Логично предположить, что pcx-файл должен содержать 5·4 байтов (рис.8а) без учета размера заголовка. Линия белого цвета, и поэтому требуется инициализация 4-х слоев видео-памяти. Но практика показывает (рис.8б,в), что оба редактора поступают по-другому и размеры pcx-файла больше, чем ожидалось. За счет чего же происходит увеличение размера?
В файле, созданном редактором PMTV (A4TECH) видим (рис.8в), что для каждого слоя строка содержит (начиная с 4 колонки и до конца) дополнительную информацию, относящуюся к незанятой нашим изображением части экрана. То есть, иными словами, в файле хранится дополнительная информация о всей строке экрана независимо от размеров изображения по горизонтали.
В файле же, созданном PC Paintbrush, мы наблюдаем то же самое, за исключением того, что хранимая информация зависит от размеров изображения по горизонтали и выбирается кратной целому числу.
Казалось бы, во втором случае подход более рационален, так как размер файла меньше. К сожалению, дело омрачается тем обстоятельством, что в байте, расположенном после после байтов с изображением (рис. 8б, колонка 4, выделено жирным), оказывается "мусор", и если не принимать никаких мер, то справа от изображения появится красивая, но ненужная бахрома. "Мусор" образуется из-за того, что буфер с записываемыми данными предварительно не очищается, и неинициализированные байты могут содержать любую информацию.
С4h,FFh,07h,C3h,00H - 0-й слой
С4h,FFh,07h,C3h,00H - 1-й слой
С4h,FFh,07h,C3h,00H - 2-й слой
С4h,FFh,07h,C3h,00H - 3-й слой
а) предполагаемый результат
С4h,FFh,07h,01h,C5h,00H - 0-й слой
С4h,FFh,07h,03h,C5h,00H - 1-й слой
С4h,FFh,07h,21h,C5h,00H - 2-й слой
С4h,FFh,07h,32h,C5h,00H - 3-й слой
б) PC Paintbrush Zsoft Corp.
С4h,FFh,07h,FFh,00H ... - 0-й слой
С4h,FFh,07h,FFh,00H ... - 1-й слой
С4h,FFh,07h,FFh,00H ... - 2-й слой
С4h,FFh,07h,FFh,00H ... - 3-й слой
в) PMTV фирмы A4TECH.
Рис.8. Содержимое pcx-файлов
Значение поля 14 (bplin) в случае PC Paintbrush равно здесь 11 байтам, оно зависимосит от размеров изображения. Для редактора PMTV оно постоянно и всегда равно 80 байтам.
Принятие мер выражается в увеличении программного кода, что конечно же пустяки для PC Paintbrush или другой большой по размеру программы, но что плохо для задач типа той, которую решал автор (см. второе условие в приведенном выше списке требований к программе).
Автору кажется, что в этом смысле подход, реализованный в PMTV (A4TECH) более рационален. Во-первых, потому, что алгоритм кодирования информации, принятый для pcx-файлов, далек от совершенства и выигрыш так мал, что его можно и не заметить. Во-вторых, сокращается размер кода программы и упрощается обработка pcx-файла.
Основная цель кодирования - сжатие информации. Метод кодирования в файлах PCX дает результат намного худший, чем например метод, принятый в файлах типа GIF, TIFF и других.
На этом завершим комментарии к условиям задачи и перейдем к составлению и обсуждению блок-схемы:
В исходном тексте нашей программы отсутствует такой подробный разбор, так как для данной задачи вышеописанные условия известны заранее. Поэтому после подготовки данных можно установить необходимый видеорежим дисплея, вывести изображение и остановиться в ожидании команды пользователя. По команде восстановить исходный режим и завершить программу (см. блок-схемы на рис.9).
Основная программа OPCX#4. Процедура EGAP

Рис.9. Блок-схемы
Рассмотрим теперь, что происходит в процедуре EGAP.
Поскольку к данному моменту заголовок файла PCX прочитан, мы приступаем к подготовке данных.
Определяем количество байтов, отведенных на строку. Это позволит определить, когда заканчивается строка и нужно ли переключать слой видеопамяти. Формируем маску для очистки "мусора". Неизбежность этого связана с тем, что "мусор" может присутствовать и в последнем байте, относящемся к изображению, а нам необходимо очистить только "загрязненные" биты. Например, для байтов в колонке номер 7 белой линии длинною в 75 пикселов необходима маска "07h":
1 2 3 4 5 6 7
С4h,FFh,С4h,FFh,C1h,FFh,17h - 0-й слой
С4h,FFh,С4h,FFh,C1h,FFh,37H - 1-й слой
С4h,FFh,С4h,FFh,C1h,FFh,07H - 2-й слой
С4h,FFh,С4h,FFh,C1h,FFh,47H - 3-й слой
Рис.10
Преобразовываем и устанавливаем палитру. "Сообщаем" адаптеру дисплея о том, что вывод изображения будет происходить в послойном режиме. И начинаем ...
Вывод изображения
Блок-схема вывода изображения, представленная на рисунке 11, достаточно подробна и не требует пояснений. Следует только сказать, что в этой процедуре есть дополнительные проверки, связанные с необходимостью отслеживать количество прочитанных и выведенных байтов. Это необходимо для того, чтобы правильно осуществлять постепенную подкачку данных из файла PCX.
Внимание не заострялось на этом моменте, так как это не являлось основной темой.

Рис.11. Процедура SETSCR
На этом можно закончить описание блок-схем программы, а читателю посоветовать приступить к изучению исходного текста программы и экспериментам с ним: opcx.zip (3588 байт).
Чтобы упростить читателю знакомство с исходным текстом программы, он снабжен комментариями и оформлен в виде программы типа .COM с возможностью передачи программе имени обрабатываемого pcx-файла через командную строку, и полностью готов к ассемблированию.
Литература:
1. Описание пакета PCX Programmer's Toolkit Genus Microprogramming, February 1, 1989.
Читайте также другие статьи Андрея Бордачева: Как изменить способности компьютера и Макросы First и Second.
Статьи по Assembler
Internet : assembler
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
[+] Softpanorama
Страница Николая Безрукова, разработанная в рамках образовательного проекта Softpanorama. (Николай Безруков в течение 7 лет был главным редактором бюллетеня этого проекта.)
Комментированный сборник ссылок на международные ассемблерные ресурсы, полезные для разных категорий программистов. Содержание:
Страница обновляется эпизодически. Последние материалы относятся к 1999 - 2000 году.
[+] Programmers' Heaven
Крупный сервер по программированию. Владелец - Synchron Data. Обновляется регулярно. Содержит оригинальные материалы по ассемблеру:
В разделе Demos большое количество разнообразных демонстрационных программ. Разделы, посвященные ассемблеру, в основном содержат материалы по системному программированию, непосредственной работе с компьютером на уровне архитектуры, решению математических задач. Рассматривается архитектура не только x86, но и 680, и др. Материалов по прикладному программированию (в т.ч.для Windows) очень мало.
[+] NASM - The Netwide Assembler Project
Сайт о проекте NASM - свободно распространяемом ассемблере для x86. Содержание:
[+] John Fine's home page
Сайт о программировании в защищенном режиме и системных утилитах. Автор - John Fine, модератор конференции comp.lang.asm.x86. Сборник:
[+] Icey-Dee's Programming Site
Статьи и учебные пособия по программированию на ассемблере. Авторы - "Martin Marietta"(?), "vulture", "vla", "asphixia" (Grant Smith). Материалы относятся в основном к конецу 80-х - началу 90-х годов. Содержание:
[+] PC-GPE on the Web
Автор - Brand Huntsman. Страница содержит только один учебник для начинающих "VLA Tutorial" - тот же самый, что на Icey-Dee's Programming Site.
[+] Assembly languages
Сайт об ассемблере, автор - John English. Содержание:
[+] ASSEMBLE IT! - ASM orientad page
Страница об ассемблере, автор - Ferdi Smit. Содержание:
Страница создана в 1996 году, давно не обновлялась, многие ссылки мертвы
[+] MoonWare Home Page
Давно заброшенная (1996) страница известного разработчика Raymond'а Moon'а. Содержит довольно приличный сборник ссылок на старые ассемблерные ресурсы, подборку утилит (в основном архиваторы) и FAQ (те же, что в Assembly languages).
[+] Open Directory:Top:Computers:Programming:Languages:Assembly
Ассемблерный раздел грандиозного проекта Netscape под названием Open Directory, призванного ответить на вызов, брошенный сетевым каталогам взрывообразным ростом интернета. Идея заключается в том, что каталог Open Directory должны пополнять добровольные редакторы, которыми могут быть любые люди. Пока не очень получается: в разделе чуть больше 10 ссылок. Имеются подразделы:
[+] Chris Dragan's Corner
Персональная страница Chris'а Dragan'а. Содержит несколько авторских freeware-программ. Имеет зеркало некоторых материалов из Assembly Programming Journal.
[+] HandiLinks
Разделы сетевого каталога HandiLinks: ассемблер (10 ссылок), ассемблеры для процессоров: x86 (30 ссылок), IBM 370 (16), Z80 (15), 68k (1), Amiga (3)
[+] Pila (assembly for Pilot)
Сайт Darrin Massena об ассемблере Pila для компьютера Pilot (процессор 68000). Обновляется очень редко (несколько раз в год). Pila - это консольное приложение, работающее под Win95/NT, сочетающее в себе кросс-платформенный компилятор и компоновщик (с возможностями компоновки ресурсов). На сайте имеются:
[+] PC Assembly Language tutorial
Сборник небольших оригинальных статей по программированию на ассемблере, сгруппированных в разделы для начинающих (более 100 статей), для опытных (30 статей), для продвинутых (20 статей). Имеется также раздел по web-программированию (40 статей). Материалов по работе в win32 нет, но имеется много полезных и интересных статей, начиная от истории компьютеров и кончая работой с оборудованием. Некоторые из статей представляют собой сборники ссылок на ассемблерные ресурсы.
hardware
[+] DDJ Microprocessor Center
Проект журнала Dr.Dobbs Journal о процессорах x86. Статьи, руководства по процессорам (от 80186 до PIII) и материнским платам, сборники ссылок, недокументированные возможности, ошибки и прочее. Сайт содержит подробнейшую информацию по самым разным аспектам программирования процессоров Intel, в основном в виде журнальных статей.
[+] Chip directory
Крупнейший сервер о процессорах, чипах и чипсетах. Вся необходимая документация от производителей для разработчиков: глоссарий, справочники, схемы, книги, ссылки. Имеется лист рассылки. Общий объем сайта в архиве - 3,6 Мбайт. Также распространяется на CD-ROM. Имеет зеркало в России.
[+] The Operation Systems resource center
Персональная страница, владелец - Chris Lattner. Существует с 1995 года, пополняется регулярно. Содержит большое количество полезной информации по работе с аппаратными средствами и программному обеспечению нижнего уровня (кроме Windows), в том числе:
Другие страницы ссылок на assembler.ru:
ассемблер
братские сайты
документация
разное
Internet : win32asm
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
[+] Iczelion's Win32 Assembly Homepage
Самый известный в Интернет ресурс о программировании на ассемблере для Windows. Регулярно обновляется и расширяется. Разделы:
Win32 Assembly - сборник статей (более 30) о программировании для Windows
VxD Tutorials - основы разработки виртуальных драйверов устройств для Windows
формат PE-файла и работа с ним
работа с ODBC
как писать NT сервисы на ассемблере
поддержка исключений в программах на ассемблере под Windows
как использовать DirectDraw
и др.
файл заголовков API windows.inc, постоянно обновляемый
последние обновления компилятора MASM
справочники по функциям API
специализированный редактор программиста UltraEdit
и пр.
[+] Assembly Programming Journal
Сетевой журнал о программировании на ассемблере. Выходит с октября 1998 каждые три месяца и содержит статьи и код для начинающих, опытных и продвинутых программистов на ассемблере. Редактор - mammon_. Ведущие разделов - Iczelion, mammon_, Xbios2. Кроме статей, сайт содержит большой раздел ссылок на ассемблерные ресурсы, форум, библиотеку примеров, программное обеспечение, в том числе бесплатное для программирования под win32:
[+] Whiz Kid Technomagic by G. Adam Stanislav
Автор - G. Adam Stanislav, независимый программист, Rhinelander, Wisconsin. Сайт открыт 10.01.1997. Содержит примеры исходных текстов на ассемблере для Windows 95:
Мнение автора:
Избранное : документы
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
русский язык
[+] В.И.Юров "Справочная система по языку ассемблера IBM PC"
Приложение к книге В. Юрова, С. Хорошенко "Assembler: учебный курс", изд-во "Питер".
HTML - руководство. Доступно как в онлайне, так и в zip-архиве (738 Кбайт). Хорошо структурировано, снабжено большим количеством схем. Содержит справочник по базовой системе команд процессора, разделы по архитектуре процессора, основам программирования на ассемблере, макросредствам и директивам транслятора TASM. (www.dore.ru)
[+] А. Фролов, Г.Фролов "Защищенный режим процессоров Intel 80286/80386/80486"
Серия Библиотека Системного Программиста Том 6. Формат html.
Учебно-справочное пособие. Имеет практическую направленность. В книге описаны особенности защищенного режима работы процессора, процедура переключения в защищенный режим и возврата в реальный режим. Приведены программы, демонстрирующие использование защищенного режима и мультизадачность. Описана иерархия средств для работы в защищенном режиме, в частности, интерфейсы прерывания BIOS INT 15h, VCPI, DPMI, DOS-экстендеры и виртуальная машина операционной системы Windows. beda.stup.ac.ru/psf/ziss/wmaster/index.htm
[+] П.Абель "Ассемблер и программирование для IBM PC"
285 Кбайт в архиве zip, текстовый формат DOS
Широко известная книга по программированию на ассемблере под MS-DOS. Хороша для освоения ассемблера "с нуля". Отличается тем, что изложение принципов и приемов ассемблирования дано чрезвычайно подробно, с массой примеров и обсуждением всевозможных особенностей и аспектов. Слово "Windows" в мегабайтном тексте не встречается ни разу. Можно скачать еще и отсюда и из многих других мест.
английский язык
[+] Randall Hyde "The Art of Assembly Language Programming"
1270 Кбайт, формат html
Классический труд, один из лучших в мире учебников программирования на ассемблере. Имеет ярко выраженную практическую направленность. Последовательно освещает все аспекты программирования на ассемблере, от базовых понятий для начинающих до тонкостей работы с оборудованием и оптимизации программ для продвинутых программистов. К сожалению, программирование для Windows в книге не обсуждается. В Сети имеется много других мест, кроме авторского сайта Рэндалла Хайда, откуда можно скачать эту книгу. Например, отсюда.
[+] "Iczelion's Win32 Assembly Tutorial"
207 Кбайт в архиве rar, формат html
Самое известное в Сети руководство по написанию программ для Windows на ассемблере. Единственное в своем роде по полноте изложения материала. Оригинал на авторском сайте Iczelion'a постоянно добавляется новыми статьями, их уже более 30. Содержание статей рассчитано на программистов, имеющих опыт работы на ассемблере и знакомых с архитектурой Windows.
[+] Таблица кодов 80x86
Очень удобная в использовании мнемоническая таблица кодов. Формат .pdf (142 Кбайт), распечатывается на двух сторонах формата A4. Автор - Roger Jegerlehner.
Другие страницы ссылок на assembler.ru:
ассемблер
братские сайты
assembler
разное
Избранное : форумы
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
рунет
[+] fido7.ru.asm.chainik
Конференция Fidonet (доступна через Usenet), русский язык, средняя интенсивность 10 сообщений в сутки, средний объем сообщения 2,5 Кбайт. Уровень - в основном для начинающих программистов на ассемблере. Основная тематика - собственно язык ассемблера и программирование для DOS. Для конференции характерны все свойства эхоконференций FidoNet: специфичный язык, обильное цитирование, пониженная содержательность.
[+] fido7.talks.asm
Конференция Fidonet (доступна через Usenet), русский язык, средняя интенсивность более 30 сообщений в сутки, средний объем сообщения 2,5 Кбайт. Уровень - для программистов, имеющих опыт работы на ассемблере. Основная тематика - программирование для DOS. Для конференции характерны все свойства эхоконференций FidoNet.
internet
[+] alt.lang.asm
Конференция Usenet, английский язык, средняя интенсивность 15 сообщений в сутки, средний объем сообщения 2 Кбайт. Уровень - в основном для опытных программистов, но новички тоже приветствуются. Основная тематика - собственно язык ассемблера. Как правило, обмен мнениями очень содержательный. Флейм, бессмысленный треп, спам и аттачи практически отсутствуют. Цитирование в рамках приличия. Участвуют корифеи: Randall Hyde, Al Leitch, Ray Moon. Одна из самых полезных конференций.
[+] alt.os.assembly
Конференция Usenet, английский язык, средняя интенсивность 2-3 сообщения в сутки, средний объем сообщения 2 Кбайт. Уровень - в основном для малоопытных программистов. Нарушения нетикета минимальны. Дискуссия практически не соответствует заявленной теме (ОС), а в основном касается различных аспектов программирования на ассемблере, часто - тривиальных. В целом содержательность невелика.
[+] comp.lang.asm.x86
Конференция Usenet, английский язык, средняя интенсивность более 30 сообщений в сутки, средний объем сообщения 2 Кбайт. Уровень - для опытных программистов. В отличие от alt.lang.asm, обсуждение не касается языка; в основном речь идет о работе на нижнем уровне в среде x86, почти инвариантно к операционной системе. Содержательность очень высока, нарушения нетикета отсутствуют. Гуру: Randall Hyde, Peter Anvin, Aviv Revach, Al Leitch.
[+] Whiz Kid Technomagic by G. Adam Stanislav
Автор - G. Adam Stanislav, независимый программист, Rhinelander, Wisconsin. Сайт открыт 10.01.1997. Содержит примеры исходных текстов на ассемблере для Windows 95:
Мнение автора:
[+] de.comp.lang.assembler.misc
Конференция Usenet, английский и немецкий язык, средняя интенсивность менее 1 сообщения в сутки, средний объем сообщения 1,5 Кбайт. Уровень и тематика неопределенны. Содержательность низка.
[+] de.comp.lang.assembler.x86
Конференция Usenet, немецкий язык, средняя интенсивность более 30 сообщений в сутки. Остальные параметры оценить не удается, так как автор не шпрехен зи дойч. Если кто-нибудь знает, о чем и как говорят в этой конференции, его квалифицированное мнение будет помещено здесь с благодарностью.
[+] microsoft.public.masm
Конференция Usenet, английский язык, средняя интенсивность менее 5 сообщений в сутки, средний объем сообщения 1,8 Кбайт. Уровень - для малоопытных и начинающих программистов на ассемблере. Тематика - собственно язык, различные компиляторы (не только MASM) и среды разработки. Содержательность довольно высока, нарушения нетикета невелики. Участвуют Randall Hyde, Ray Moon, John Fine.
Избранное : разное
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
[+] Криптография от Black Wolf'а
Обширный и интересный сборник материалов о современной криптографии на сайте Black Wolf'а. Русский. Введение в криптографию, описание алгоритмов, криптоанализ, исходные тексты и программы, в общей сложности несколько десятков статей.
Другие страницы ссылок на assembler.ru:
ассемблер
братские сайты
assembler
документация
Рунет : ассемблер
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
[+] Домашняя страничка Алексея Фрунзе
Алексей программирует на Borland Pascal, TASM, MASM, NASM, Watcom C и DJGPP. Сайт обновляется регулярно. Содержит авторские разработки:
Имеется большая подборка разнообразной документации (в виде zip-архивов):
[+] RPC Software
Сайт о программировании. Обновляется еженедельно, имеется рассылка новостей и форум. В разделе об ассемблере несколько документов на русском языке:
В разделе "Программы и исходники" - демо-версия игры "Марио", пример вируса
[+] SOFT MUSEAM
Персональная страничка неизвестного автора. Содержит несколько небольших программ на ассемблере для DOS. Одна из программ представляет собой компилятор-компоновщик com-файлов для разработанного автором элементарного ассемблера с простым и интересным синтаксисом.
[+] Компания Free Programmers
Компания Free Programmers - это трое единомышленников, занимающихся разработкой собственной операционной системы FPOS. ОС находится в начальной стадии, также как и сайт. Разработчики имеют опыт программирования процессора в защищенном режиме.
[+] Страница Панькова Дмитрия
Страница о программировании на ассемблере для DOS. Содержит материалы:
[+] AsmJournal на русском
Сайт существует с ноября 1999 года, автор - Heir To Empire. Основным содержанием сайта должны являться русские переводы выпусков журнала Assembly Programming Journal - одного из самых ценных ассемблерных ресурсов в Сети. В настоящее время в списке числятся восемь выпусков журнала - с октября-1998 по март-2000. Кроме того, сайт имеет раздел по программированию для Unix/Linux и раздел ссылок. К сожалению, проверить, насколько содержание сайта соответствует заявленному, оказалось невозможным. Сайт обильно использует JavaScript и все внутренние ссылки неработоспосбны ни в IE, ни в NN. По крайней мере, у нас.
[+] Проект "Компьютерный Клуб"
Автор - Александр Гацко. Сборник документов и ссылок по программированию. Раздел об ассемблере содержит более 20 ссылок на документы, размещенные на различных англоязычных сайтах. Тематика документов касается в основном архитектуры и применения процессоров Intel и AMD. Из русскоязычных документов имеются в ссылках на www.intel.ru:
Также имеются документы по криптографическим алгоритмам, распознаванию образов, математике, базам данных, языкам программирования, операционным системам, программированию под Windows, под MS-DOS и пр.
Арихтектура и дизайн сайта имитируют разветвленное файловое дерево. Поиск необходимого раздела иногда требует довольно большого количества шагов.
hardware
[+] CHIPINFO
Совместный проект организаций и предприятий, специализирующихся в области разработки, производства и продажи электронных компонентов: НПФ "Радио-Сервис", "Макро Тим", МТУСИ, "Оптоника", "ПромАвтоКонтракт", "Точка Опоры", "АВ-Центр", "Квазар-Микро", New Technologies, "Паллада-Микро". CHIPINFO - пожалуй, единственный на сегодняшний момент сервер, имеющий собственную базу данных по электронным компонентам с возможностью полнотекстового поиска информации об электронных компонентах как по наименованию, так и по функциональным возможностям. При работе с сервером доступны:
каталоги, сборники
[+] List.ru:Компьютеры:Программирование:Языки программирования:Ассемблер
Категория сетевого каталога List.ru. Список зарегистрированных сайтов об ассемблере.
[+] List.ru::Ассемблер:FAQ и учебники
Подкатегория сетевого каталога List.ru - FAQ и учебная литература.
[+] Коллекция WEB-мастера
Маковеев Сергей, web-мастер сайта кафедры "Защита информации в системах связи" ПГУ, собрал обширную коллекцию книг, документов, статей, исходных кодов, журнальных выпусков и прочих материалов по компьютерной тематике. Среди них: все книги А.Фролова и Г.Фролова, журнал Internet Zone, большая подборка по защите информации, документация по ОС Unix, Windows, DOS, базам данных, языкам Java и JavaScript, HTML, DHTML и XML, Visual Basic, C/C++, Delphi, Shell, Clarion, службам и протоколам Internet, аппаратному обеспечению и пр. Кроме того, сайт поддерживает зеркала известных серверов www.citforum.ru, www.hackzone.ru и www.linux.org.ru. К сожалению, материалов по ассемблеру на сайте нет.
[+] Русские документы
Обширный сборник русскоязычной компьютерной документации. Раздел по ассемблеру содержит около 30 документов, из них касаются Windows около 10. Интересны несколько авторских статей Ашота Оганесяна, раскрывающих тонкие и недокументированные возможности работы в Windows. Состав остальных документов обычный: справочники по TASM (2.0, 3.0) и MASM, программирование под DOS, работа с мышью, видеоадаптером, программирование резидентов и т.д.
[+] FileArea
Сборник различных файлов, в том числе текстовых документов. Специального раздела по ассемблеру нет, но в разных разделах имеется большое количество интересных документов:
объявления
Привет всем!
Кто занимается 2D и 3D графикой на ассемблере, пишите все, кого интересует графика на ассемблере на мой ящик Cheremgtk@mail.ru, будем вместе обсуждать и решать возникающие проблемы, а также обмениваться своим опытом, сообщать об своих успехах, короче быть в центре всех событий по графике на ассемблере.
С уважением,
Шойко Александр
Другие страницы ссылок на assembler.ru:
братские сайты
assembler
документация
разное
Рунет : братские сайты

HI-TECH - Сайт изобретателей фонариков на солнечных батарейках
Сайт о низкоуровневом программировании. Его базой служит почтовая рассылка на сервере Subscribe.Ru, которая называется "Низкоуровневое программирование для дZeнствующих" и целиком посвящена программированию на ассемблере, начиная с самых фундаментальных его основ.
Предыдущие выпуски рассылки:
![]() |
Подпишитесь на рассылку с помощью этой формы! |
Кроме этого, на сайте имеется обширный раздел DOWNLOAD, содержащий:
Рекомендуем также посетить еще один сайт Алексея http://welcome.to/pmode/ - о программировании защищенного режима. На сайт выложены почти два десятка примеров программ, от простого к сложному, что важно - написанных на бесплатных реализациях ассемблера, с комментариями и пояснительным текстом.

Отдел Исследования Программ
Это один из самых популярных в Рунете ресурсов о программировании.
Наиболее часто обновляемый раздел сайта — Новости познакомит вас с текущим состоянием информационного рынка, здесь же вы узнаете об изменениях, происходящих на сайте.
В разделе Инструменты вы найдете большое количество программ, которые позволят успешно решить множество разнообразных задач: от сжатия готового исполняемого модуля или его шифрации, до восстановления исходного текста программы из исполняемого модуля.
Наиболее интересный раздел на сайте — Библиотека. В нем вы найдете фрагменты из лучших книг по программированию.
В разделе Статьи вы найдете небольшие материалы разных авторов по программированию и компьютерной технике.
В разделе Исходные тексты размещаются тексты программ на разных языках программирования, демонстрирующие приемы программирования и способы реализации алгоритмов.
Другие страницы ссылок на assembler.ru:
ассемблер
assembler
документация
разное
Ссылки
рунет
win32asm - программирование на ассемблере для Windows (8)
ассемблер - программирование на ассемблере (14)
братские сайты - сайты настоящих ассемблерщиков, с которыми у нас установились теплые дружеские отношения (3)
internet
win32asm - программирование на ассемблере для Windows (3)
assembler - программирование на ассемблере (18)
избранное
форумы - конференции, форумы, рассылки, эхи из Usenet, Internet, Рунет, Fidonet (8)
документы - книги, документация, статьи (6)
разное - интересные ресурсы, имеющие косвенное отношение к программированию на ассемблере (1)
Статьи по Assembler
Артефакты
В таком контексте - интересные программы, сделанные неизвестно для чего - впервые применил термин "артефакт" Serrgio в соответствующем разделе братского нам Сайта изобретателей фонариков на солнечных батарейках. Нам это название показалось настолько удачным, что мы тоже применили его, не задумываясь. Надеемся, что вам оно также понравится, и вы посвятите всю свою жизнь созданию артефактов и закончите ее в нищете, но посмертно будете признаны великим программистом.
Само слово "артефакт", придуманное неизвестно кем неизвестно в какие времена и означающее "сделанный искусственно", ввел в современный обиход, скорее всего, Артур Кларк в своей великой "Одиссее-2001". Там, если помните, так назывался черный обелиск - носитель внеземного мессиджа и одновременно средство для превращения Юпитера в еще одно солнце. А может быть, это был Клиффорд Саймак, но у него столько великих книг, что мы не припомним, в которой это случилось. Как бы там ни было, потом слово размножилось на страницах тысяч фантастических романов и навязло в зубах.
Возможно поэтому, когда 2001 год оказался не за горами, а полет к Юпитеру по-прежнему остался уделом фантастики, словечку легко нашлось новое применение. Теперь им обозначают всякие полезные штучки, от куска колбасы до трансцендентирующего суперплазмогана, находимые геймерами в лабиринтах бесчисленных шутеров. Наверное, у этого применения термина тоже есть конкретный автор, но мы его не знаем.
салют
цветочки
Это технологическая версия предыдущей программы - на ней отлаживались траектории движения огней. Но поскольку она имеет кое-какую самостоятельную красоту, мы ее включили сюда отдельным пунктом.
галактика
Эта программа может служить тестом вашей способности к ментальной связи с компьютером, то есть, грубо говоря, позволяет определять, дайвер вы или нет (© С.Лукьяненко). Для этого:
Если полученное вами число плюсиков больше 71 - у вас есть дайверские способности. И чем больше это число, тем ваши способности больше.
Если ваш тест прерван приездом бригады скорой помощи, продолжите его после выписки из психлечебницы.
Все три перчисленные выше программы написаны в 1993 году на TASM 3.1, когда автор по мере сил участвовал в развале отечественной оборонной отрасли. Основной обязанностью сотрудников нашего НИИ было своевременное получение аванса и зарплаты. В перерывах между этими мероприятиями мы эксплуатировали обе 286-е машины нашей лаборатории. На одной автор писал программули, подобные этим, а на другой девчонки резались в покер.
3d-мандельброт
А вот этот артефакт любезно прислал нам Ivan Grinkin (спасибо ему большое!). Эта вещь в некотором смысле уникальная, настоящий handmade! Если вам кажется, что вы зря отвалили деньги за PIV-1400, и он в вашем компьютере занимается в основном тем, что, вяло обмахиваясь кулером, от нечего делать гоняет пару-тройку байт взад-вперед по кэшу, дайте ему пожевать программу Ивана. Гарантируем: мало не покажется. Она в реальном времени разрисовывает стены трехмерного лабиринта фракталом Мандельброта. (А если же у вас процессор послабее, то, значит, пора апгрейдиться.) Короче, качайте 3d.rar (3901 байт), да не забудьте прочитать вложенную инструкцию, а то не будете знать, куда ползти.
Mycall (ассемблер)
Эта программа разработана в рамках проекта assembler.ru для демонстрации различий между приложением win32, написанным на ассемблере, и аналогичным приложением написанным на C++.
См. полный исходный текст обоих вариантов приложения, комментарии к нему и анализ различий.
Copyright © 2000 Авторские права защищены и распространяются на исполняемый модуль, исходный текст и документацию. Разрешено свободное распространение. Распространение на возмездной основе любого вида запрещено. Любая модификация разрешена только в целях личного пользования. Распространение в модифицированном виде запрещено. Распространение исполняемого модуля без документации запрещено. Пользователь применяет программу под собственную ответственность. Автор не несет ответственности за проблемы, которые могут возникнуть у пользователя в связи с применением программы.
Будем благодарны, если об ошибках и недостатках, а также замечания и предложения вы сообщите по адресам email, указанным в заголовочной части документа.
Приложение MyCall заменяет штатный интерфейс "Удаленный доступ к сети" (Remote Access Service, RAS) Windows 95/98 в части, используемой при выполнении соединения. При этом имеют место следующие преимущества и дополнительные возможности:
Кроме того, приложение MyCall имеет следующие особенности:
Поскольку приложение MyCall разрабатывалось как учебное, в нем, с целью сохранения компактности, не реализованы некоторые полезные возможности:
ВНИМАНИЕ! Логины и пароли хранятся в файле mycall.txt в открытом виде. Будьте осторожны и не применяйте приложение MyCall в ситуациях, когда доступ к файлу mycall.txt могут получить лица, способные воспользоваться им ненадлежащим образом. Например, не рекомендуется использовать MyCall на чужом компьютере, компьютере коллективного пользования или офисном.
Несмотря на указанные недостатки, двухмесячная (на момент публикации) постоянная эксплуатация приложения MyCall показала его достаточную функциональность. Приложение полезно в ситуациях, когда пользователь имеет активный доступ в Интернет через нескольких провайдеров, с несколькими учетными данными, через несколько разных телефонных номеров модемных пулов.
Приложение поставляется в виде zip-файла mycalla.zip размером 11471 байт. Для его установки необходимо:
Mycall (c++)
Эта программа разработана в рамках проекта assembler.ru для демонстрации различий между приложением win32, написанным на C++, и аналогичным приложением написанным на ассемблере.
См. полный исходный текст обоих вариантов приложения, комментарии к нему и анализ различий.
Copyright © 2000 Авторские права защищены и распространяются на исполняемый модуль, исходный текст и документацию. Разрешено свободное распространение. Распространение на возмездной основе любого вида запрещено. Любая модификация разрешена только в целях личного пользования. Распространение в модифицированном виде запрещено. Распространение исполняемого модуля без документации запрещено. Пользователь применяет программу под собственную ответственность. Автор не несет ответственности за проблемы, которые могут возникнуть у пользователя в связи с применением программы.
Будем благодарны, если об ошибках и недостатках, а также замечания и предложения вы сообщите по адресам email, указанным в заголовочной части документа.
Приложение MyCall заменяет штатный интерфейс "Удаленный доступ к сети" (Remote Access Service, RAS) Windows 95/98 в части, используемой при выполнении соединения. При этом имеют место следующие преимущества и дополнительные возможности:
Кроме того, приложение MyCall имеет следующие особенности:
Поскольку приложение MyCall разрабатывалось как учебное, в нем, с целью сохранения компактности, не реализованы некоторые полезные возможности:
ВНИМАНИЕ! Логины и пароли хранятся в файле mycall.txt в открытом виде. Будьте осторожны и не применяйте приложение MyCall в ситуациях, когда доступ к файлу mycall.txt могут получить лица, способные воспользоваться им ненадлежащим образом. Например, не рекомендуется использовать MyCall на чужом компьютере, компьютере коллективного пользования или офисном.
Несмотря на указанные недостатки, двухмесячная (на момент публикации) постоянная эксплуатация приложения MyCall показала его достаточную функциональность. Приложение полезно в ситуациях, когда пользователь имеет активный доступ в Интернет через нескольких провайдеров, с несколькими учетными данными, через несколько разных телефонных номеров модемных пулов.
Приложение поставляется в виде zip-файла mycallc.zip размером 11569 байт. Для его установки необходимо:
Пакет logcount
| Программный пакет LOGCOUNT - это набор программных средств для счета, учета и анализа посещаемости www-сайтов. Пакет прост в установке и использовании. Требования к уровню подготовки пользователя при работе с пакетом минимальны. Программы, входящие в состав пакета, компактны, быстры и занимают минимум ресурсов www-сервера и рабочей станции пользователя. Пакет ориентирован на применение на небольших корпоративных и персональных сайтах. | ![]() ![]() |
С примерами использования пакета LOGCOUNT вы можете ознакомиться на сайтах:
Чтобы воспользоваться пакетом, скачайте файл logcount.zip (150 Кбайт), распакуйте его в рабочий каталог на своем компьютере и выполните инструкции по установке, находящиеся в документе log-manu.chm.
назначение пакета
| Например, на Assembler.Ru выполнено if(hit){document.write(hit);}else{document.write("?");} хитов. |
| Например, на Assembler.Ru побывали if(hit){document.write(session);}else{document.write("?");} посетителей. |
| Например, сейчас на Assembler.Ru if(hit){if(visitors==1){document.write(visitors+" посетитель");}else{document.write(visitors+" посетителей");}}else{document.write("?");}. |
/li>
- WebTrends Log Analyzer
- Analog
- 10-Strike Log-Analyzer
и других.
состав пакета
Пакет LOGCOUNT поставляется в виде упакованного zip-файла logcount.zip (
|
Размещается на www-сервере в каталоге cgi-bin. |
|
Размещается на www-сервере в каталоге cgi-bin. |
|
Размещается на рабочем месте пользователя. |
|
Размещается на рабочем месте пользователя. |
|
Размещается на рабочем месте пользователя. |
|
Размещается на рабочем месте пользователя. |
|
Размещается на рабочем месте пользователя. |
|
Размещается на рабочем месте пользователя. |
|
Размещается на рабочем месте пользователя. |
системные требования
Единственное требование для работы скрипта log.pl - это возможность установки и исполнения пользовательских скриптов на языке Perl на www-сервере.
Операционная среда сервера значения, как правило, не имеет. Скрипт показал работоспособность на серверах под управлением ОС Windows NT 4.0 (Internet Information Server) и Unix (Apache).
Для работы пакета не требуется использование директив включения на стороне сервера (SSI, Server Side Includes). Этим он отличается в лучшую сторону от большинства других текстовых счетчиков, так как существенно снижается нагрузка на сервер.
Exe-программы, входящие в состав пакета - это консольные приложения Win32. Они работоспособны в операционных системах:
технические характеристики
Скрипт log.pl обеспечивает минимальную загрузку сервера при своем исполнении. Измеренная на непрерывном потоке запросов, она по времени занятия процессора всего в два раза выше, чем у "пустого" скрипта, не имеющего в своем составе ни одной команды.
Скрипт выполняет счет посещений вне зависимости от того, включена ли загрузка графики в броузере пользователя, и поддерживает ли броузер выполнение скриптов на языке JavaScript.
Тестирование скрипта, проведенное на маломощном сервере (Pentium-200, Windows NT 4.0, RAM 48 Mбайт, IIS) при отсутствии сторонней нагрузки, показало, что он обслуживает без потерь поток до 80 запросов в секунду. В реальных условиях этот показатель может существенно отличаться как в одну, так и в другую сторону. Однако можно быть уверенным, что скрипт обеспечит обработку посещаемости до нескольких тысяч или десятков тысяч хитов в сутки, что в большинстве случаев достаточно для персональных сайтов и сайтов для малого бизнеса.
Скрипт выдает информацию для счетчиков в виде значений переменных языка JavaScript, доступных для обработки в документах HTML, представляющих страницы сайта. Благодаря этому веб-мастер может обеспечить показ значений счетчиков на страницах в текстовом виде, применяя для их оформления любые средства HTML и CSS. Опытный веб-мастер может также использовать эти значения для создания графических счетчиков с раздельным представлением разрядов и сравнительных диаграмм.
Показания счетчиков, поддерживаемых скриптом, имеют смысл:
Здесь: хит - загрузка страницы в броузер для просмотра пользователем, хост - компьютер пользователя, с которого осуществляется просмотр сайта. Характеризуется уникальным ip-адресом
Лог-файл, формируемый скриптом log.pl, очень компактен. На каждый хит в лог-файле приходится запись фиксированной длины 20 байт. Для сравнения - длина записи в лог-файле NSCA - переменная, и может достигать нескольких сотен байт. Кроме того, в лог-файл NSCA-формата включаются записи о запросах не только страниц сайта, но и не несущие полезной информации записи о запросах элементов оформления. Таким образом, размер лог-файлов, формируемых скриптом log.pl, в десятки раз меньше, чем размер стандартных лог-файлов, формируемых, например, www-сервером Apache, при практически той же информативности. Это дает существенную экономию трафика и времени при закачке лог-файлов для последующего анализа и архивирования. Кроме того, практически исключена ситуация переполнения лог-информацией лимита серверного дискового пространства, с которой часто приходится сталкиваться при использовании штатных лог-файлов сервера.
Exe-программы написаны на ассемблере и поэтому обеспечивают максимальную скорость обработки. Например, утилита log1read.exe обрабатывает 1000000 записей менее чем за 8 секунд (PII-266, Windows 98). Эта скорость особенно важна при статистической обработке, и в частности, при составлении рейтингов ip-адресов, когда требуется выполнять сортировку многих тысяч записей.
условия использования
Пакет поставляется как DONATION WARE. Это означает, что пользователь имеет возможность добровольно передать автору через российский регистратор SHAREG сумму 150 рублей, предназначенную для поддержки дальнейшего развития пакета. Прежде чем использовать программы пакета, необходимо ознакомиться с полным перечнем условий использования, содержащимся в Лицензии пользователя. Если вы не согласны с какими-либо из условий, вы не должны использовать программы пакета.
Индивидуальная поддержка предоставляется пользователям на взаимной основе.
Программы
Cодержащиеся здесь программы являются авторскими разработками и находятся под защитой международных соглашений и российских законов.
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
| [+] интерфейс Remote Access Service для Windows 95/98 (C++) | mycallc | |
| Грубо говоря, dialup-звонилка. Разработана как учебно-экспериментальная задача в рамках работы над assembler.ru. Тем не менее имеет некоторые полезные потребительские свойства. Может быть интересна людям, имеющим много провайдеров, много логинов, плохую связь (все как у автора) и не любящим наворотов. Самое главное - очень маленькая, меньше 9 Кбайт. | ||
| [+] интерфейс Remote Access Service для Windows 95/98 (assembler) | mycalla | |
| Программа, абсолютно идентичная предыдущей, но написанная на masm. Кстати, поскольку задача учебно-экспериментальная, для обоих вариантов на assembler.ru опубликованы исходные тексты. Так что тот, кто все-таки любит навороты, может их понаворачивать сам, но с соблюдением условий, приведенных в прилагаемой документации. | ||
| [+] пакет LOGCOUNT | LOGCOUNT | |
| Счетчик посещений сайта, совмещенный с логом, счетчиками страниц и системой рейтингов посещаемости. Сверхомпактная запись логов - всего 20 байт на хит. Минимальные требования к серверу и веб-мастеру. В комплекте - набор утилит для скачивания лог-файлов с сайта, преобразования их в текстовый формат и формат NCSA, статистического анализа, анализа поисковых запросов. Пример использования счетчика - титульная страница сайта assembler.ru. FREEWARE! | ||
| [+] Салют, цветочки и галактика | артефакты | |
| Этим маленькие программульки были написаны в далеком уже 1993 году, еще до второго путча. Пользы в них столько же, сколько оказалось в итоге у всех этих долгих лет: интересно, но бессмысленно. |



Программирование: Языки - Технологии - Разработка
- Программирование
- Технологии программирования
- Разработка программ
- Работа с данными
- Методы программирования
- IDE интерфейс
- Графический интерфейс
- Программирование интерфейсов
- Отладка программ
- Тестирование программ
- Программирование на Delphi
- Программирование в ActionScript
- Assembler
- Basic
- Pascal
- Perl
- VBA
- VRML
- XML
- Ada
- Lisp
- Python
- UML
- Форт
- Языки программирования




