Статьи по 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 для работы с ассемблерным проектом. Специфика организации рабочей среды такова, что имеет смысл рассматривать два варианта настройки:

  • рабочая среда (workspace) содержит один проект
  • рабочая среда содержит несколько проектов


  • Вариант с одним проектом:

  • Создайте и настройте рабочий проект так, как указано в статье "ms devstudio - среда разработки asm"


  • В диалоге Project Settings на вкладке Custom Build в командную строку компилятора включите опцию:
    /FR"Debug\$(InputName).sbr"Это необходимо сделать для всех asm-файлов, для которых вы хотели бы иметь включенным средство Browser Info.

    Как видно, sbr-файлы будут помещаться в папку Debug с именем, соответствующим имени asm-файла. Эта папка выбрана нами потому, что по умолчанию в нее же помещаются sbr-файлы модулей, написанных на C/C++. Учтите, что в начале работы с проектом она не существует, поэтому попытка компиляции даст ошибку. Создайте ее вручную.
  • Если проект смешанный и содержит также файлы С/C++, включите для них опцию Browser Info установкой чекбоксов в диалоге Project Settings на вкладке C/C++ так, описано выше в этой статье.
  • Если проект чисто ассемблерный, то следует обеспечить доступ к заголовочным файлам C/C++ windows.h с тем, чтобы использовать их как справочные при построении собственного файла windows.inc. Для этого:




  • создайте файл brinfo.cpp и включите его в проект
  • содержимое файла brinfo. cpp должно представлять собой единственную строку:
    #include

  • включите для этого файла опцию Browser Info как для обычного C++-файла
  • откомпилируйте его


  • В диалоге Project Settings на вкладке Browse Info:


  • установите чекбокс Build browse info file


  • установите чекбокс Suppress startup banner


  • дополните содержимое поля Project options следующим текстом:
    Debug\*.sbr Таким образом вы дадите утилите bscmake.exe команду включить в сборку все sbr-файлы, содержащиеся в папке Debug

  • Выполните компиляцию всех модулей проекта, а затем его компоновку. Browser Info готово к работе.

    Следует учесть одно неприятное обстоятельство. Рабочая среда определяет необходимость запуска утилиты bscmake.exe по факту выполнения компиляции хотя бы одного файла C/C++. Ассемблерные же файлы, будучи компилируемы посредством Custom Build, к сожалению, такой команды рабочей среде не дают. Поэтому все изменения в составе идентификаторов в ассемблерных файлах остаются втуне до тех пор, пока не будет перекомпилирован хотя бы один cpp-файл проекта и после этого не выполнена его компоновка. В чисто ассемблерных проектах придется вручную вызывать компиляцию файла brinfo.cpp.

    В этой беде мог бы помочь вынос вызова утилиты bscmake.exe на этап Post-build step. Однако, этот фокус не проходит: дело в том, что однажды активизировавшись, Browse Info открывает bsc-файл и держит его в дальнейшем в открытом состоянии, запрещая таким образом запись в него всем внешним программам. Этот запрет отменяется только на этапе компоновки проекта, но не на этапе Post-build step.

    Вариант с несколькими проектами базируется на тех же идеях, что и вариант с одним проектом, но с учетом некоторых коррекций:


  • прежде всего следует определиться, имеется ли необходимость иметь единое для всех проектов пространство идентификаторов (не в смысле программирования, а в смысле просмотра их с помощью Browse Info), или у каждого проекта должно быть свое пространство, или проекты можно разбить на группы, с отдельным пространством для каждой группы
  • для каждого пространства следует определить одну папку, в которой должны содержаться все sbr-файлы модулей, относящихся к этому пространству
  • для каждого пространства следует использовать отдельный файл brinfo.spp




  • Инструкция программиста mycall


    Приложение MyCall - это пользовательский интерфейс Remote Access Service для Windows 95/98. Приложение разработано как учебно-экспериментальная задача, целью которой является демонстрация отличий в реализации приложений на ассемблере и на C++. См. статью Зачем он нужен, этот ассемблер?.
    Исходный текст реализации на C++ состоит из файлов:
  • main.cpp - основной файл
  • main.h - файл заголовков
  • mycall.rc - файл описания ресурсов
  • icon1.ico - иконка

  • Полный комплект файлов, необходимых для компиляции приложения, содержится в zip-файле mycallcb.zip (13192 байта)
    Исходный текст реализации на ассемблере состоит из файлов:
  • main.asm - основной файл
  • main.inc - файл заголовков
  • @struct.inc - файл структурных макросов
  • windows.inc - файл заголовков win32
  • mycall.rc - файл описания ресурсов
  • icon1.ico - иконка

  • Полный комплект файлов, необходимых для компиляции приложения, содержится в zip-файле mycallab.zip (15913 байта)
    компиляция приложения
    Компиляция реализации на C++:
  • Создайте в MS Developer Studio новый проект приложения win32 ("Win32 Application") с именем mycall
  • Распакуйте файл mycallcb.zip, поместив извлеченные из него файлы в папку проекта
  • Подключите к проекту файлы main.cpp и mycall.rc
  • Установите в качестве активной конфигурации для построения проекта "Win32 Release"
  • Измените установки проекта для сборщика (Settings/Link):

  • удалите из списка подключаемых библиотек все, кроме kernel32.lib и user32.lib
  • добавьте в список rasapi32.lib
  • отключите библиотеки по умолчанию (флажок "Ignore all default libraries")
  • установите в качестве точки входа функцию WinMain (поле "Entry-point symbol" в категории "Output")

  • Выполните построение проекта
    Компиляция реализации на ассемблере:

  • Создайте в MS Developer Studio новый проект приложения win32 ("Win32 Application") с именем mycall
  • Распакуйте файл mycallab.zip, поместив извлеченные из него файлы в папку проекта
  • Подключите к проекту файлы main.asm и mycall.rc. (Учтите, что файл main.asm становится виден в окне файлового диалога MS Developer Studio только в режиме "Все файлы")
  • Установите в качестве активной конфигурации для построения проекта "Win32 Release"
  • Измените установки для компиляции файла main.asm (Settings/Custom Build):


  • Это, в свою очередь, диктует особенности программных решений. Был сознательно выбран хаотический стиль программирования, не использующий практически никаких характерных для 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. Она обрабатывает сообщения:


  • WM_COMMAND - поступающее при изменении позиции списков
  • WM_USER - используемое при мониторинге состояния соединения для принятия решения о необходимости повтора дозвона
  • WM_MOVE - для запоминания позиции окна при его перемещении
  • WM_DESTROY и WM_CLOSE - для выполнения завершающих операций при окончании работы приложения
  • регистрируемое глобальное сообщение my_message - для обеспечения взаимодействия экземпляров приложения



    После выбора соединения, телефона и логина, пользователь, нажав кнопку, переводит приложение в режим дозвона. При этом:


  • блокируются все органы управления приложением, за исключением кнопки, которая начинает служить для прекращения дозвона
  • вызывается функция ras_dial, которая осуществляет дозвон

    Функция 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]
    Здесь:
  • /options - необязательный перечень опций (см.таблицу ниже)
  • filename - имена файлов исходных текстов, подлежищих компиляции, разделенные пробелами. В среде MS Developer Studio обычно указывается только одно имя в виде шаблона $(InputPath).
  • /link linkoptions - командная строка компоновщика в случае, если используется опция /Bl. В среде MS Developer Studio обычно не применяется.


  • опция назначение применение для 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[=text] 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-программа" обычно возникают три вопроса:
  • Как она работает?
  • Как сделать собственную stub-программу?
  • Как уменьшить ее до размера, не раздражающего настоящего ассемблерщика?

  • Как она работает? Чтобы ответить на этот вопрос, следует заглянуть во внутренности формата PE-файла - стандартного исполняемого модуля Windows. Там мы обнаружим следующее:
  • PE-файл содержит в себе две программы, склеенные друг вслед за дружкой. Первая из них и есть stub-программа в обычном для MS-DOS exe-формате. Вторая - приложение для Windows.
  • stub-программа, как и положено в MS-DOS, начинается в файле с нулевого смещения и первые два байта представляют собой знаменитые инициалы MZ, коими увековечил себя кто-то из корифеев Microsoft (не могу навскидку вспомнить. Кто знает - напишите) (16.05.01 написали. Михаил Орлов сообщил, что это был Mark Zbikovski. Спасибо им обоим.) Можно сказать, вырезал свое имя на каждом пне. С этого имени начинается стандартный заголовок exe-файла MS-DOS.
  • windows-программа начинается с некоторого смещения, кратного параграфу (16 байт) и зависящему от размера stub-программы. Первые два байта windows-программы - это инициалы какого-то другого мужика со странным именем Portable Executable. Далее следует заголовок PE-файла и все остальные его элементы.

  • 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)
    <


    Действительно, ни отнять, ни прибавить: только в голову настоящего ассемблерщика могут придти трюки, позволяющие так, чтобы помягче сказать, использовать формат исполняемого файла. Еще раз, вот они:

  • Когда делается попытка запустить программу из-под DOS, загрузчик, определив по сигнатуре "MZ", что это исполняемый файл, считывает по смещению 08h размер exe-заголовка, а по смещению 14h стартовое значение регистра ip, чтобы узнать, где находится исполняемый код. В предложенном Grief'ом варианте stub'а длиной 20h он таким образом выясняет, что передавать управление следует на смещение 10h (просто выход в DOS посредством функции 4Ch), а в варианте длиной 18h - на смещение 0Ch (здесь перед выходом успевает еще напечататься восклицательный знак). И неважно, что занятые исполняемым кодом байты на самом деле являются полями exe-заголовка "Максимум требуемой памяти за концом программы", "Сегментное смещение сегмента стека", "Значение регистра sp" и "Контрольная сумма исполняемого модуля". Все эти поля в данном случае не влияют работу программы.
  • Если же программа запускается из-под Windows,то загрузчик, опять же разобравшись с назначением файла, первым делом считывает двойное слово по смещению 03ch, чтобы узнать, где находится PE-заголовок. Так вот, здесь это смещение оказывается внутри самого PE-заголовка! В варианте "18h" оно попадает на поле, содержащее суммарный размер секций неинициализированных данных в образе приложения, а в варианте "20h" - на поле, содержащее размер секций кода. Использование этих полей в современных нам версиях Windows неочевидно. По одним источникам - они используются для первичного отведения памяти под приложение. По другим - не используются вообще. Во всяком случае, практическая проверка показывает, что приложения с таким stub'ом работают нормально. (Что будет дальше - пожалуй, неизвестно даже Microsoft'у.)

    Конечно же, заставить какой-нибудь компоновщик создать исполняемый файл с предлагаемыми 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, и все вдруг понимают, почему двери в психушках открываются исключительно вовнутрь. Вывалив языки и тяжко дыша, братия расползается по своим кельям зализывать раны и готовиться к грядущим битвам. Посередине вытоптанной поляны, одинокий, жалкий и забытый, остывает трупик несчастного новичка
  • когда новичок, отчаявшись познать истину самостоятельно или испить ее из ладоней великих, записывается на компьютерные курсы, и, с трудом досидев на первом занятии до сакраментального "У кого есть вопросы?", тянет руку и получает в ответ краткое изложение всей программы обучения. Затем, сверившись со списком группы и перезвонив в кассу, преподаватель также дает обещание, что к концу курса студент сам сможет элементарно ответить на этот простейший вопрос.

  • Между тем вопрос о минимальной программе - совсем не простейший, и представляет отнюдь не академический интерес. Минимальная программа, очевидно, решает две задачи: (1)стартует и (2)завершается в конкретной рабочей среде. И то, и другое она должна делать корректно, с тем, чтобы:


    В результате компиляции и сборки этого приложения получается исполняемый файл размером 1536 байт. Из них собственно код занимает 10 байт. Все остальное - на совести создателей формата PE-файла. Приложение безропотно запускается и не задумываясь завершает свою работу, возвращая код выхода.

    Что здесь что и зачем?

  • .386 - директива ассемблера, определяющая набор команд процессора, который может быть использован в программе. Для приложений win32 необходимо использовать эту директиву или выше, в зависимости от того, собираетесь ли вы использовать возможности, предоставляемые процессорами последующих поколений
  • .model flat,stdcall - сегментная директива ассемблера, определяющая сегментную модель приложения как плоскую, использующую соглашения о вызове процедур, принятые в win32. Именно такая сегментная модель должна всегда использоваться при написании приложений для win32
  • ExitProcess PROTO :DWORD - прототип функции API, выполняющей завершение приложения. (Поскольку сервис этой функции предоставляется dll-библиотекой kernel32.dll, то при сборке приложения хорошо бы не забыть подключить библиотеку импорта kernel32.lib)
  • .code - сегментная директива ассемблера, определяющая начало сегмента кода
  • WinMain PROC PUBLIC hinst,prev_hinst,command_line,cmd_show - начало тела стартовой процедуры. Следует обратить внимание на наличие директивы видимости (visibility) PUBLIC, которая позволит сборщику сделать процедуру доступной для операционной системы, дабы та смогла передать управление приложению. Параметры процедуры обсудим чуть ниже, правда, новость будет не очень приятной.
  • ;... - здесь должна быть всякая прочая мелочь, которую вы хотели бы заставить делать ваше приложение
  • invoke ExitProcess,0 - собственно вызов функции завершения приложения. Здесь код выхода 0, но он, естественно, может быть любым в пределах 32-разрядного целого. В полноценных приложениях нормой считается определение кода выхода в цикле обработки сообщений главного окна.
  • WinMain ENDP - всякое тело должно иметь конец. Это он и есть. Правда, в силу наличия предыдущей строки, он в данной программе не достижим, но это и не требуется: здесь мы просто исполняем необходимую формальность. Кстати, настоящим ассемблерщикам должно быть приятно, что, опустив ненужную в связи с этим команду ret, мы сэкономили аж 4 байта кода!
  • end - конец модуля.




  • Замечание 1. По поводу необходимости применения функции ExitProcess. Именно с ее помощью, а не посредством команды ret, как могли бы ожидать знатоки C/C++, должно завершаться приложение win32, написанное на ассемблере.

    Если залезть внутрь runtime-библиотеки C/C++, то мы увидим, что именно так завершает работу приложения уже упоминавшаяся функция _WinMainCRTStartup. Но поскольку подключать runtime-библиотеку C/C++ к ассемблерной программе как-то нелогично (хотя и вполне возможно), мы должны вызвать ExitProcess "вручную". Только эта функция корректно завершает работу приложения, в частности, оповещая использовавшиеся приложением библиотеки DLL о необходимости декрементировать их счетчики занятости и, при обнулении последних, выгрузиться из памяти.

    Замечание 2. А вот и обещанная плохая новость про параметры стартовой процедуры. Дело в том, что установкой их значений занимается - кто бы вы думали? - опять же _WinMainCRTStartup! И следовательно, в ассемблерных программах, где ее нет, при входе в WinMain параметры содержат:

  • hinst - не дескриптор текущего экземпляра приложения, а количество акций Microsoft у Билла Гейтса
  • prev_hinst - не 0, а прогнозируемое значение NASDAQ после выхода на мировой рынок российской ОС BrokenWindows
  • command_line - не указатель на командную строку, а традиционный народный российский указатель направления
  • cmd_show - не предлагаемый начальный вид окна, а то, что обещал показать мировому сообществу Никита Сергеевич Хрущов (и даже, помнится, начал раздеваться)


  • Так что, воленс-ноленс, придется получать эту ценную информацию самостоятельно. Как говорится, клик хере.

    Замечание 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 в варианте Visual C++. Никаких особенностей здесь нет - все должно быть так же, как для программирования на C++
  • Найдите где-нибудь MASM 6.11с или более поздний. Чистый 6.11 (без "c") лучше не использовать: есть у него кое-какие проблемы. (В частности, выравнивает структуры максимум на 4, а в win32 часто требуется выравнивать на 8.) Сейчас щедрый американский дядюшка позволяет пользоваться MASM бесплатно, а совсем недавно приходилось покупать его за хорошие зеленые деньги. Есть он, например, в составе MSDN. Или его можно скачать из Сети (только не с ftp.microsoft.com. Рыбные места вы сможете найти в разделе ссылок нашего сайта). По минимуму из всего пакета потребуется один лишь модуль ml.exe размером около 400 Кбайт.
  • Поместите ml.exe в папку исполняемых модулей пакета MS DevStudio (по умолчанию - C:\Program Files\DevStudio\VC\BIN).


  • Ваш пакет MS Developer Studio готов к разработке приложений win32 на MASM.

    Любознательный читатель спросит: почему бы не использовать для разработки приложений inline-ассемблер, являющийся, как известно, неотъемлемым атрибутом C++. Вообще-то ничего невозможного на свете нет. Можно и отверткой в ухе ковыряться, только не очень глубоко. Но надо помнить, что inline-ассемблер предназначен для улучшения языка C++, а не для разработки самостоятельных приложений. Поэтому из него исключены большинство директив и, самое обидное, все макросредства, сильно помогающие облегчить процесс программирования.

    Следующий шаг - это подготовка включаемых файлов:

  • Получите файл includes.zip


  • Распакуйте его и поместите файлы @struct.inc и windows.inc в папку включаемых файлов пакета MS DevStudio (по умолчанию - C:\Program Files\DevStudio\VC\INCLUDE).


  • Файл @struct.inc - это авторская отсебятина, которой в принципе можно и не пользоваться, а можно и наоборот, добавить в него что-нибудь свое, полезное. Что и для чего в этом файле, вы можете прочитать здесь.

    С файлом windows.inc все гораздо сложнее. Это файл заголовков API, ассемблерный аналог той тучи h-файлов, которые содержатся в пакете Visual C++ и начинаются с файла windows.h (и, возможно, не только). Поскольку Microsoft официально не поставляет файл windows.inc (в отличие от windows.h), у ассемблерного программиста в связи с этим возникает большая-пребольшая проблема. Ее можно попытаться решить несколькими путями:

  • транслировать windows.inc из windows.h с помощью специальной программы-переводчика H2INC, поставляемой в составе пакета MASM 6.11+. Сразу скажем, что следует смело отправить эту надежду в мусоропровод. Продравшись сквозь частокол багов H2INC, вы обнаружите, что ее интеллекта хватает лишь на трансляцию тривиальнейших заголовочных файлов, к которым windows.h, безусловно, не относится. Вы утонете в бездне сообщений о некорректном переводе, и ваш сизифов труд по их обходу будет вознагражден получением совершенно негодного результата
  • найти windows.inc (вариант - win.inc) в Интернете. Это гораздо более реально. Существует некоторое количество чудаков (возможно, это дети миллионеров, которым наплевать на необходимость зарабатывать на жизнь), которые тратят свое время на формирование и актуализацию этого файла и раздают его всем желающим. Некоторых из них вы найдете в разделе ссылок. Есть мнение, что пользоваться такими файлами настоятельно не рекомендуется. Представьте себе ситуацию к концу второго месяца возни этого доброго человека с каким-нибудь пятым по уровню вложенности заголовочным файлом в пирамиде windows.h. Возьмет, да и перепутает с устатку порядок описания членов какой-нибудь структуры. Или забудет указать ее выравнивание относительно умалчиваемого. Хорошо еще, если соответствующая функция API при использовании ее вами будет вываливаться с фатальной ошибкой вроде нарушения общей защиты. А может ведь и не вываливаться. И будете вы разбираться, почему глючит ваше приложение, дольше, чем добрый дядя переводил заголовочный файл
  • описывать структуры, объявлять прототипы и делать все прочие действия, которые делает windows.h, внутри собственного проекта. Это еще более реальный вариант. Для этого вам потребуется оперативный доступ к содержимому windows.h из своего проекта (см.ниже). Каждый раз, как у вас возникнет необходимость обратиться к какой-нибудь функции API, вы должны будете найти в windows.h все, что касается ее вызова, и включить это в свой проект, предварительно переведя вручную на язык ассемблера. При наличии небольшого навыка такой перевод выполняется довольно быстро и проблем не вызывает
  • делать все то же самое, но не в рамках текущего проекта, а в едином файле windows.inc, чтобы не повторять эти действия в последующих проектах. Со временем необходимость править windows.inc будет возникать у вас все реже и реже




  • Именно последний вариант и предлагается в качестве основного. Файл windows.inc, который мы вам предложили выше, взят из состава приложения MyCall. В нем только самая малая часть из того, что должно быть, и есть кое-что лишнее (RAS, например, в Visual C++ имеет собственный заголовочный файл). Но для старта этого достаточно.

    Не забудьте только включить свой windows.inc в процедуру архивирования (надеемся, таковая у вас имеется). Будет обидно начинать все сначала после какой-нибудь очередной переинсталляции системы.

    Ну вот, а теперь пора создать новый проект:

  • Запустите MS DevStudio
  • Выберите в меню: File/New


  • В появившемся диалоге выберите вкладку Projects


  • В списке типов проектов выберите Win32 Application


  • В поле Location укажите каталог, в который предполагаете поместить папку проекта
  • В поле Project Name укажите желаемое имя проекта. Учтите, что по умолчанию это имя будет присвоено исполняемому модулю и, кроме того, папке, в которой будут находиться файлы проекта на этапе его разработки. (Поле Location автоматически дополняется именем папки проекта.)
  • По остальным элементам диалога убедитесь, что DevStudio собирается создавать для вашего проекта новое рабочее пространство (Create new workspace) для платформы Win32
  • Нажмите кнопку OK


  • Новый проект создан.

    В рамках вновь созданного проекта первым делом необходимо создать файл с исходным текстом:

  • Нажмите кнопку New Text File или создайте текстовый файл посредством диалога, вызываемого через меню File/New


  • Сохраните вновь созданный файл командой меню File/Save As. При этом дайте файлу желаемое имя с расширением .asm, например, main.asm. В принципе, расширение тоже может быть любым, но лучше не отходить от общепринятых стандартов
  • Подключите файл к проекту. Для этого, например, можно в окне Workspace на вкладке FileView щелкнуть правой кнопкой мыши по заголовку пока пустого списка файлов проекта и в появившемся контекстном меню выбрать пункт Add Files to Project. В файловом диалоге, чтобы увидеть наш новый файл, придется переключиться в режим Все Файлы.




  • А теперь самое важное. Файл когда-нибудь придется компилировать. Подготовиться к этому следует уже сейчас:

  • В окне Workspace на вкладке FileView щелкните правой кнопкой мыши по имени файла
  • В появившемся контекстном меню выберите пункт Settings. Появится диалог Project Settings в режиме установок для файла
  • В диалоге выберите вкладку Custom Build


  • В списке Settings for выберите пункт Win32 Debug


  • В текстовом поле Build command(s) введите новую команду:
    ml.exe /c /coff /nologo /Zi /DDEBUG $(InputPath)


  • В текстовом поле Output file(s) введите текст:
    .\$(InputName).obj


  • В списке Settings for выберите пункт Win32 Release


  • В поле Build command(s) введите новую команду:
    ml.exe /c /coff /nologo $(InputPath)


  • В поле Output file(s) введите текст:
    .\$(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
    Вот теперь можно смело нажимать кнопку Compile (Ctrl+F7). Компиляция файла должна пройти без ошибок.

    Обратите внимание, что имя включаемого файла windows.inc указано с полным путем. Это не случайно. В процессе разработки приложения вам придется очень часто обращаться к этому файлу, добавляя в него заголовочную информацию. В MS DevStudio это сделать очень просто, причем несколькими способами. Например, можно выделить имя файла и, щелкнув по нему правой кнопкой, выбрать пункт контекстного меню Open Document. Но удобнее все-таки подключить файл windows.inc к проекту "на постоянно", воспользовавшись правой кнопкой мыши в окне Workspace. Если вы удачно попадете по имени проекта или по какой-нибудь папке, вам станет доступна опция Add files to Project (Folder)... С ее помощью-то вы и подключите файл к проекту, и в дальнейшем простым двойным кликом сможете вызывать его на редактирование.



    Теперь перейдем к сборке проекта:

  • В окне Workspace на вкладке FileView щелкните правой кнопкой мыши по заголовку списка файлов (он же имя проекта)
  • В появившемся контекстном меню выберите пункт Settings. Появится диалог Project Settings в режиме установок для проекта
  • В диалоге выберите вкладку Link


  • В списке Settings for выберите пункт Win32 Debug


  • В списке Category выберите пункт General


  • В поле Output file name вы можете при желании указать имя создаваемого исполняемого модуля, если вас не устраивает имя по умолчанию
  • Установите флажок Ignore all default libraries. Таким образом вы откажетесь от подключения runtime-библиотеки C/C++ и сэкономите несколько десятков килобайт кода. Подробнее см. здесь и здесь.
  • В списке Category выберите пункт Output


  • В поле Entry-point symbol укажите WinMain (в данном случае) - имя функции, являющейся точкой входа для проекта
  • В списке Settings for выберите пункт Win32 Release


  • Проделайте все то же самое по п.п.6...9


  • Все, проект готов к сборке. Нажмите кнопку 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, а:

  • MASM32
  • Win32api.hlp
  • Aditor v2.x (текстовый редактор)
  • парочку утилит
  • SoftIce


  • В аргументах MemoBreaker'а есть многое, с чем можно согласиться. Например, с тем, что MASM32 - действительно идеальное средство для разработки ассемблерных приложений под Windows. И входящий в его состав файл windows.inc, который весит сейчас уже больше 800 Кбайт - действительно освобождает программиста от необходимости создавать этого монстра самому, потому что сделан достаточно хорошо, со знанием специфики и, что самое главное, постоянно обновляется. И компилятор ml.exe, который входит в пакет - всегда самой последней версии. И это при всем при том, что Microsoft не имеет к MASM32 никакого отношения (ну разве что компилятор родом оттуда). Потому что MASM32 - это детище независимых разработчиков во главе с Iсzelion'ом.

    Вы всегда можете найти самую свежую версию пакета MASM32 на его официальном сайте. Кстати, дизайн сайта достаточно красноречиво говорит о дворянском происхождении MASM32, что, впрочем, ничуть не умаляет уважения к титаническому труду создателей пакета.

    Но некоторые положения статьи, которые вызывают возражения у наших корреспондентов, хотелось бы пояснить.

    Собираясь ставить эксперимент по сравнению ассемблерной и сишной версий MyCall, мы изначально хотели наглядно показать преимущества ассемблерной программы. У нас совсем не было планов показывать, что никаких радикальных преимуществ нет, а вот очевидные недостатки (в первую очередь - большая трудоемкость) - имеются. Мало того, мы ведь вложили в этот эксперимент несколько больше, чем вы, дорогие читатели, в прочтение нашей статьи. И поэтому вы должны представить себе наше разочарование конечными результатами.

    Проведенный эксперимент совершенно не претендует на роль истины в последней инстанции. MemoBreaker предлагает провести несколько сравнений: от минимального приложения до большого проекта, и только тогда делать выводы. Наверное, такой масштабный эксперимент дал бы много свежей пищи для размышлений. К сожалению, ничего подобного мы нигде не встречали ранее, да и вряд ли встретим когда-либо. По нашим наблюдениям, то, что сделали мы с MyCall - единственная реальная попытка подтвердить (или опровергнуть?) на практике так часто выдвигаемый тезис о прекрасной приспособленности ассемблера для создания приложений для Windows.



    И если абстрагироваться от оптимистических оценок, и опираться только на сухие результаты этого единственного эксперимента, то мы вынуждены повторить неутешительный вывод, уже сделанный в оригинале статьи. Нет никаких причин разрабатывать приложения для Windows на ассемблере, за исключением одной-единственной: личного предпочтения разработчика.

    Другое дело, следует ли считать эту причину существенной. По нашему мнению - да, стоит. Потому что мы глубоко убеждены, что склонность к программированию на ассемблере - это не следствие образования или опыта работы, а в первую очередь следствие типа мышления данного конкретного индивидуума. И этот тезис мы повторяли и будем повторять на страницах сайта. Начиная с шуточного психологического теста и кончая этими вполне серьезными рассуждениями.

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

    К сожалению, дар абстракции дан людям не в равной степени. Наверное, в этом проявляется рудиментарное наследие каменного века, когда одни индивидуумы абстрактно загоняли мамонтов в ловчие ямы, а другие вполне конкретно мочили несчастных сверху булыжниками и дубинами. В результате у первых больше развилось интуитивное правое полушарие головного мозга, а у вторых - рациональное левое. И теперь, по причине отсутствия мамонтов, первые занялись программированием на C++, а вторые - на ассемблере.

    Еще раз сформулируем нашу позицию касательно размера приложения, выработанную в результате эксперимента с MyCall и на основании собственного опыта разработки приложений для Windows:

  • Разработка небольших приложений на ассемблере не дает ощутимых преимуществ по объему кода по сравнению с C++, а вернее, C, потому что в таких приложениях объектно-ориентированные возможности C++ обычно неприменимы. Пример тому - MyCall.
  • Что касается больших приложений, то однозначно выявить преимущество ассемблера перед C++ по объему кода практически не представляется возможным. В таких приложениях существенную роль играют диктуемые языком принципы организации программ. Ассемблер предлагает программисту хаотический стиль, и приходится применять усилия и специальные приемы (см., например, статью Лептонный стиль программирования) для преодоления этого недостатка языка. C++, напротив, стимулирует формирование у программиста объектно-ориентированного стиля, и лишь неспособность или нежелание воспринять его приводит к отказу от этого стиля в пользу хаотического. А по-разному организованные программы становятся практически несравнимыми, даже если решают одну и ту же задачу.
  • Еще более неочевидным становится гипотетическое преимущество ассемблера в объеме кода, если вспомнить, что приложения для Windows далеко не полностью состоят собственно из кода. Более того, в массе случаев код занимает в них меньшую часть. А гораздо больший объем занимают разнообразные данные: ресурсы, структуры, таблицы и прочее, прочее, прочее. Понятно, что данные в своем большинстве инвариантны к языку, и, следовательно, их наличие маскирует преимущество ассемблера в компактности кода.
  • Плюс к сказанному: современные компиляторы C++ (в частности, Visual C++), при установленном флажке "Оптимизация по объему" дают практически оптимальный код, лишь в малой степени уступающий коду, написанному вручную на ассемблере. Более того, простая небрежность, лень или неполное знание ассемблерщика способны легко разрушить его призрачное преимущество. А вот глупому компилятору C++ такие человеческие слабости незнакомы - он оптимизирует код всегда.




  • Похожие аргументы мы высказываем и при рассмотрении преимуществ ассемблера в быстродействии:

  • Многозадачная операционная среда, каковой является Windows, распределяет ресурсы производительности компьютера отнюдь не по признаку того, на каком языке написано приложение. Вытесняющая многозадачность, не спрашивая желания нашего приложения, выделяет ему тот временной ресурс, который считает нужным, а остальное время отдает другим потокам, маскируя то преимущество в быстродействии, которое мог бы дать ассемблерный код.
  • Развитый сервис API, освободивший ассемблерщика от огромного объема рутинной работы, играет в данном случае злую шутку: вызовы API обрабатываются с одной и той же скоростью вне зависимости от того, на каком языке написано приложение, и в силу этого также стирается преимущество в скорости ассемблерного кода.
  • Такой же эффект производит и система сообщений, без которой не обходится ни одно уважающее себя оконное приложение. Ее обслуживает очень приличный кусок кода, локализованного вне приложения, и отнимающего у приложения свою немалую долю ресурса производительности.
  • В случае разработки приложений с активным использованием графики большая часть гигантской работы по ее обслуживанию все больше ложится на "железо" - 2d и 3d ускорители, которым также безразлично, на чем написано наше приложение.
  • Наконец, современные компиляторы C++ при установленном флажке "Оптимизация по времени" генерируют практически оптимальный код, который дает максимальную скорость так же независимо от настроения и знаний программиста, как и минимальный размер в случае оптимизации по размеру.


  • В подтверждение этих тезисов приведем письмо, полученное от Dmitry S. Bobrik (спасибо ему!) буквально во время написания этой статьи:

    Hello!

    Можете себе представить - компиленый Сишный код работает БЫСТРЕЕ написаного на Асме!

    Единственное разумное объяснение - оптимизатор MSVC пишет код учитывая особенности архитектуры процессоров Пентиум (ну и выше...). А на Асме все то ручками, да ручками... обидно даже :-/

    wbr, Dmitry

    http://bcsoft.da.ru

    http://home.tula.net/frazez

    <


    Дмитрий сообщил, что впервые об этом эффекте он прочитал в мэйл-листе sp-forth@egroups.com

    И несколько замечаний в заключение:

  • Это не похороны ассемблера, хотя бы потому, что:


  • ему по-прежнему есть и будет место в разработке драйверов устройств
  • ему по-прежнему есть и будет место в приложениях, монополизирующих ресурсы системы:


  • в играх
  • в обработке потоков данных, в том числе в реальном времени
  • в расчетах
  • и т.д.


  • вы будете смеяться, но Windows - совсем не единственная операционная система


  • Это не похороны ассемблера, даже несмотря на пришествие технологии .NET, славной такой компактностью исходного кода, которая не снилась никакому ассемблеру.
  • Это не похороны ассемблера хотя бы потому, что его хоронили уже бессчетное количество раз.
  • А мы программировали, программируем и будем на нем программировать.




  • Ошибки при вызове функций 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 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 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
    Здесь:
  • hInstance - дескриптор текущего экземпляра данного приложения. Это значение используется в дальнейшем для взаимодействия с элементами рабочей среды, связанными с данным приложением: ресурсами, окнами, изображениями и многими другими. Как правило, надобность в знании этого дескриптора возникает почти сразу после запуска экземпляра приложения и сохраняется на протяжении всего срока его жизни. Этот дескриптор локален в рамках данного экземпляра приложения и не имеет смысла для других приложений и других экземпляров этого же приложения.
  • hPrevInstance - должен был бы быть дескриптор предыдущего экземпляра данного приложения. Однако в win32 этот параметр не используется и всегда равен 0. Часто бывает нужно знать, существуют ли в момент запуска приложения другие его экземпляры, и в зависимости от их существования предпринимать какие-либо действия. (Например, отменить запуск нового экземпляра и вывести окно уже существующего экземпляра на передний план.) Так вот, hPrevInstance после гибели win16 стал для этого совершенно бесполезен. (См. статью взаимодействие экземпляров приложения.)
  • lpCmdLine - указатель на командную строку. Сегодня большинство обычных пользователей практически не обращают внимания на архаичные возможности управления приложениями, предоставляемые командной строкой. Однако некоторые программисты все еще используют ее (в том числе и те, которые писали Проводник). Не факт, что командная строка вам понадобится, но знать, как получить к ней доступ - полезно.
  • nCmdShow - параметр, описывающий, в каком виде рекомендуется создать главное окно приложения: скрытое, свернутое, нормальное, развернутое и т.д. Полезен в случаях, когда вызывающий процесс должен определять вид окна вызываемого процесса. Тогда nCmdShow следует использовать при первом вызове функции ShowWindow для главного окна приложения. Если же вы пишете самостоятельное приложение, то вполне допустимо проигнорировать этот параметр и показывать окно в том состоянии, которое вам больше нравится.


  • И вот все эти ценные и не очень параметры программисту на ассемблере недоступны. Сборка приложения без подключения 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:

  • работа начинается с вынужденного объектно-ориентированного действия - громоздкой регистрации класса окна с помощью функции RegisterClassExA. Вы должны сделать это, даже если собираетесь создать всего одно окно с такими свойствами, потому что так повелел когда-то Уильям Гейтс Второй, известный в народе как просто Билл. Впрочем, вы можете избежать этой неприятной процедуры, если воспользуетесь при создании окна одним из предопределенных классов - BUTTON, COMBOBOX , EDIT , LISTBOX , MDICLIENT , SCROLLBAR , STATIC. С помощью, например, класса BUTTON вы можете сделать большую-пребольшую кнопку и нажимать ее день и ночь до умопомрачения, ожидая, пока из floppy-дисковода выскочит банан. А класс STATIC очень удобен для эмуляции забора: написанное на таком окне емкое трехбуквенное слово всегда напомнит вам о необходимости вернуться к повседневным заботам.
  • если регистрация класса окна прошла успешно, можно создать само окно с помощью функции CreateWindowExA. Полученный при ее возврате в регистре eax дескриптор окна следует сохранить до лучших времен. Он потребуется очень часто для выполнения манипуляций с окном.
  • созданное окно полностью функционально за исключением одной мелочи: его не видно. Может, и в самом деле его не стоит показывать, чтобы не позориться. А иногда это действительно не нужно, например, если использовать главное окно для общей диспетчеризации приложения, а для общения с пользователем создавать другие окна. Но если вам действительно нужно показать окно, то это делает функция ShowWindow
  • следующий этап - ваш. Делайте что хотите. Можете пойти попить кофейку, а можете чего-нибудь попрограммировать. Но помните: пока еще в приложении обработка сообщений не идет, поэтому оно мертво. Свежесозданное окно торчит недвижимо на экране как символ стабильности внутриполитических процессов, и ничто не оставляет на нем следов, как на зубах Кристины Пугачевой/Орбакайте/Пресняковой/Орбакайте.
  • и только цикл опроса очереди сообщений оживит ваше окно, и кнопочки в его заголовке начнут нажиматься, вызывая разные полезные эффекты, и границы его станут послушны движениям придавленной слева мыши, и много еще чего разного можно будет сделать с этим окном.
  • потому что в вашем распоряжении теперь - оконная процедура win_procedure. Она принимает все и всяческие сообщения, адресуемые вашему окну всеми, кому не лень, и делает в ответ на них все, что вы пожелаете. В данном случае вы пожелали отправлять все эти сообщения обратно в систему, чтобы она сама делала с окном то, что повелел когда-то Билл. Единственное сообщение, на которое реагирует ваша оконная процедура - это WM_DESTROY, которое означает, что вам надоело любоваться окном, и вы нажали Alt+F4, или щелкнули по кнопочке с крестиком, или выбрали Close в системном меню. Да и поделом ему.


  • Если вы соберетесь откомпилировать приложение, вы можете сделать это с помощью MS Developer Studio, воспользовавшись нашими рекомендациями.

    Только не забудьте удалить из исходного текста либо строку include windows.inc, либо содержимое файла windows.inc, если он у вас имеется в готовом виде.


    Старт и завершение приложений


    Поэт утверждал, что-де "все начинается с любви". Поэтам вообще свойственно сказануть что-нибудь этакое не подумав, получить гонорар - и в ресторан, а вы потом разбирайтесь, чего он имел ввиду. Например, предположим, имеется пара гомо сапиенс противоположных полов, выращенная до репродуктивного возраста в условиях, гарантированно исключающих ознакомление с процессом размножения двуполых животных вообще и вида гомо сапиенс в частности. Вопрос: сможет ли указанная пара, руководствуясь исключительно собственными наблюдениями разницы физиологического строения своих организмов и инстинктом, реализовать репродуктивную функцию? Низшие существа, как-то: хомячки - могут, проверено. А мы, цари природы, сможем? Разум не помешает? Ой, что-то сомнительно. Похоже, в процессе эволюции мы прикупили себе одно средство выживания - разум, заплатив за него другим - способностью существовать вне информационного поля социума.
    Впрочем, что касается приложений win32, то здесь поэт точно был неправ. В этом случае все начинается с того, что вызывающая программа (например, Проводник) подготавливает и вызывает функцию API CreateProcess.
    Ту же задачу, в принципе, решают и устаревшие функции WinExec и LoadModule. На самом деле на сегодняшний день они представляют собой всего лишь реализации той же функции CreateProcess.
    Еще одно замечание, несколько офф-топик. Поскольку вызываемый процесс совершенно независим от вызывающего, то функция CreateProcess возвращает управление вызывающему процессу обычно до того, как вызываемый процесс закончит свою инициализацию. Если вы хотите, чтобы вызывающий процесс взаимодействовал с вызываемым, следует использовать функцию WaitForInputIdle для того, чтобы подождать завершения инициализации.
    Функция CreateProcess:
  • подготавливает для вновь создаваемого процесса рабочую среду (контекст, context), в которую входят: виртуальное адресное пространство, исполняемый код и данные, набор дескрипторов объектов, набор переменных окружения, базовый приоритет, минимальный и максимальный рабочие наборы страниц памяти
  • в процессе подготовки контекста загружает PE-файл приложения. (Большинство из перечисленных выше элементов контекста определяются содержимым этого файла.)
  • создает в рамках контекста процесса первичный поток (нить, thread) приложения и запускает его исполнение через точку входа приложения. Обычно точкой входа служит скрытая от прикладного программиста функция _WinMainCRTStartup, содержащаяся в runtime-библиотеке C/C++, подключаемой к приложению на этапе его сборки. В приложениях, написанных на ассемблере, без подключения runtime-библиотеки, точкой входа объявляется функция, подготовленная пользователем, обычно WinMain. (Подробнее...)


  • Функция _WinMainCRTStartup, если таковая имеется, подготавливает рабочую среду для runtime-библиотеки, а также значения параметров для функции WinMain, после чего передает управление функции WinMain.

    И, наконец, функция WinMain делает все, что придумает прикладной программист.

    Несколько пояснений про элементы контекста. Поскольку assembler.ru не ставит своей целью продублировать всю имеющуюся в природе документацию на Windows, то эти пояснения будут самыми общими.

  • виртуальное адресное пространство (virtual address space) - это набор адресов памяти от 0 до 0ffffffffh, доступных данному процессу. Каждый процесс имеет свое собственное виртуальное адресное пространство, одно для всех потоков процесса. Физически в каждый момент времени в оперативной памяти присутствует только небольшая часть из 4 Гбайт, доступных процессу, как правило, только то, что ему необходимо для работы. Для этого используется механизм страничной организации. Виртуальное адресное пространство разбито на страницы по 4 Кбайта. Страницы по мере необходимости загружаются в оперативную память или удаляются из нее. Это происходит автоматически, прозрачно для прикладного программиста
  • исполняемый код и данные (executable code and data) - это совокупность кода и данных, загружаемых из соответствующих секций PE-файла приложения. Одни и те же фрагменты кода могут одновременно использоваться несколькими нитями процесса
  • дескрипторы объектов (object handles) - это уникальный для данного процесса набор 32-битных идентификаторов, обеспечивающих обращение к самым разнообразным объектам рабочей среды: файлам, элементам графического интерфейса, процессам, потокам и пр.
  • переменные окружения (environment variables) - это пришедший из DOS и UNIX набор текстовых пар имя=значение. Каждый процесс может иметь собственное окружение, либо пользоваться окружением вызвавшего его процесса
  • базовый приоритет (base priority) - это значение в интервале от 0 до 31, определяющее приоритет каждого потока в многозадачной среде. В зависимости от базового приоритета распределяется процессорное время между всеми активными в данный момент потоками. Базовый приоритет формируется из класса приоритета, который присваивается каждому процессу и уровня приоритета, присваиваемого каждому потоку внутри процесса
  • минимальный рабочий набор (minimum working set) - минимально необходимый для существования активного процесса набор страниц памяти
  • максимальный рабочий набор (maximum working set) - наибольший набор страниц памяти, который может потребоваться активному процессу единовременно




  • В ОС Windows существует множество способов закончить работу приложения. В MSDN сказано, что это произойдет, если:

  • любой из потоков приложения вызовет функцию ExitProcess
  • первичный поток приложения завершится командой ret Завершение первичного потока вызовом функции ExitThread в случае наличия других потоков не приведет к завершению приложения.


  • завершится (любым способом) последний из потоков приложения
  • любой из потоков приложения вызовет функцию TerminateProcess
  • консольное приложение получит от пользователя сигнал CTRL+C (CTRI+BREAK)
  • завершится работа системы (shut down) или работа пользователя в системе (log off).


  • Надо сказать, что этот список не окончателен. Николай Критский (nkritsky@mail.ru) подсказал еще и такой экзотический, но вполне работоспособный вариант: функцией SetErrorMode отключить выдачу системных сообщений об ошибках, а затем искусственно создать такую ошибку (например, выполнив деление на 0). Возникшая при этом исключительная ситуация приведет к завершению работы приложения.

    На самом деле перечисленное разнообразие - только кажущееся, так как некоторые из указанных вариантов - это всего лишь скрытый вызов все той же функции ExitProcess. Это касается и ret в первичном потоке, и CTRL+C для консольного приложения, и даже варианта Николая Критского. Фактически в этих вариантах управление передается в существующие по умолчанию runtime-модуль, процедуру поддержки консоли или обработчик исключительной ситуации, которые и вызовут функцию ExitProcess.

    Рекомендуется завершать работу приложения вызовом функции ExitProcess. При этом гарантируется, что система будет освобождена от последствий работы приложения, а именно:

  • Все занятые приложением dll-библиотеки будут освобождены, то есть для каждой из них будет вызвана входная функция со параметрами отключения. При этом внутренние счетчики занятости библиотек будут декрементированы, и те библиотеки, счетчики которых достигнут 0, будут выгружены из системы.
  • Будут закрыты все дескрипторы объектов, существовавшие в приложении
  • Будет завершена работа всех потоков приложения
  • Объект приложения перейдет в состояние "установлен", так что если кто-то в системе ждал завершения работы нашего приложения, сразу узнает об этом
  • Все объекты потоков приложения также перейдут в состояние "установлен"
  • Статус завершения процесса изменится со значения STILL_ACTIVE на значение, переданное функции 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 эти соглашения заключаются в следующем:
  • регистр символов в имени функции не имеет значения. Например, функции с именами ILoveYou и iloveyou - это одна и та же функция. Здесь мы наблюдаем отличие от реализации MS Visual C++, где компилятор строго отслеживает совпадение регистра в именах используемых программистом функций с прототипами, содержащимися в системных заголовочных файлах. Сборщику же, как видим, регистр символов безразличен
  • аргументы передаются вызываемой функции через стек. Если аргумент укладывается в 32-битное значение и не подлежит модификации вызываемой функцией, он обычно записывается в стек непосредственно. В остальных случаях программист должен разместить значение аргумента в памяти, а в стек записать 32-битный указатель на него. Таким образом, все передаваемые функции API параметры представляются 32-битными величинами, и количество байт, занимаемых в стеке для передачи аргументов, кратно четырем
  • вызывающая программа загружает аргументы в стек последовательно, начиная с последнего, указанного в описании функции, и кончая первым. После загрузки всех аргументов программа вызывает функцию командой call
  • за возвращение стека в исходное состояние после возврата из функции API отвечает сама эта вызываемая функция. Программисту заботиться о восстановлении указателя стека esp нет необходимости
  • вызываемая функция API гарантированно сохраняет регистры общего назначения ebp, esi, edi. Регистр eax, как правило, содержит возвращаемое значение. Состояние остальных регистров после возврата из функции API следует считать неопределенным. (Полный набор соглашений stdcall регламентирует также сохранение системных регистров ds и ss. Однако для flat-модели памяти, используемой в win32, эти регистры значения не имеют.)


  • На системном уровне этот набор соглашений добавляется вот еще чем. Компилятор при формировании из исходного текста объектного файла добавляет к началу имени функции символ подчеркивания, а к концу - выражение вида @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

    Но многим гораздо больше понравится тот же самый фрагмент так, как он выглядит в MASM 6.1+:

    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



    Общая идея такова:

  • регистрируется класс главного окна и создается само окно с тем, чтобы вновь запускаемое приложение могло принимать и обрабатывать сообщения с помощью оконной процедуры win_procedure
  • регистрируется глобальное сообщение, которое впоследствии должно использоваться для обмена информацией между экземплярами приложения
  • выполняется проверка наличия уже работающего экземпляра приложения с помощью объекта mutex
  • если уже работающий экземпляр приложения существует - ему посылается инициирующее сообщение с параметром QUESTION_PRIME_HWND. Поскольку дескриптор окна этого экземпляра неизвестен, сообщение посылается широковещательно (HWND_BROADCAST).


  • ответ от уже работающего экземпляра принимает оконная процедура приложения в виде такого же сообщения, но уже с параметром ANSWER_PRIME_HWND. При этом второй параметр сообщения представляет собой дескриптор окна работающего приложения.
  • запускаемый второй экземпляр приложения, пользуясь полученным дескриптором, выводит окно первого экземпляра на первый план с помощью функции SetForegroundWindow и завершает свою работу


  • если запускаемый экземпляр приложения - первый и единственный, то он продолжает свою работу так, как задумал программист. При этом его оконная процедура всегда готова получить и обработать зарегистрированное сообщение с параметром QUESTION_PRIME_HWND, которое могут послать повторно запускаемые экземпляры. В ответ на такое сообщение работающий экземпляр отвечает таким же сообщением, но с параметром ANSWER_PRIME_HWND, передавая в качестве второго параметра дескриптор своего главного окна.

    См. также интересный, эффективный и простой вариант решения рассматриваемой задачи, предложенный Геннадием Майко.




  • 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'ом

  • Если вы уже прочитали статью Зачем он нужен, этот ассемблер? и ее обсуждение с 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 % времени работы процессоров). Но у нас не возникало даже идеи переписать все это на ассемблере! Я думаю, причина этого – требуемые очень большие временнЫе ресурсы разработки. Система просто не успела бы выйти на рынок; с другой стороны, предложения типа "поставьте мощный компьютер" не вызывали у клиентов никаких возражений.


    А вот где ассемблер действительно, по моему, нужен, так это в "недрах" операционной системы (например, при переключении задач), где может потребоваться использование специальных команд процессора, которых нет в языках программирования высокого уровня.



    Еще раз резюмируем сказанное специально для тех, до кого авторитетные мнения доходят с трудом:

  • Наиболее рационально сегодня применять ассемблер в качестве весьма ограниченного оптимизирующего дополнения к современным мощным языкам более высокого уровня, позволяющего использовать особенности процессорной архитектуры, недоступные в этих языках.
  • Ассемблерным языкам по-прежнему есть и останется место в микропроцессорной технике на уровне программирования чипов.
  • Практически нет никаких специальных причин писать на ассемблере не только приложения Windows, но и драйверы, то есть в отношении драйверов действительны те же самые соображения, что и в отношении приложений, в том числе и п.1.
  • Единственное место, где необходимость применения ассемблера неоспорима и безусловна - это самые нижние уровни ядра операционных систем.


  • Мы на 100% согласны с этими выводами, и поэтому, как раз сейчас приступая к новому прикладному проекту, уже окончательно определились с базовым языком. Конечно же, это будет ассемблер.


    Зачем он нужен, этот ассемблер?


    Читайте также:
  • обсуждение этой статьи c MemoBreaker'ом
  • дополнение Геннадия Майко

  • Вопрос о том, имеет ли смысл заниматься разработкой приложений для 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++:

  • main.cpp


  • main.h


  • Вариант на ассемблере:

  • main.asm


  • main.inc


  • @struct.inc


  • windows.inc


  • Общие для обоих вариантов:

  • mycall.rc


  • Инструкция программиста


  • Начнем сравнение с трудозатрат программиста. Непосредственно сравнить время разработки не представляется возможным, так как порядок разработки был такой: сначала была написана реализация на 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)
    В этой таблице перечислены все секции PE-файла приложения в каждой из трех реализаций: на C++ с оптимизацией по времени выполнения, на C++ с оптимизацией по размеру и на ассемблере.

    Кстати, размер исполняемого модуля в реализации C++ с оптимизацией по времени выполнения несколько больше - 9216 байт. Но при программировании приложений для Windows такую оптимизацию использовать обычно нет смысла, так как многозадачная архитектура и механизм сообщений отнимают куда больше временного ресурса, чем может дать выигрыш от нее.



    Вот что видно из этой таблицы:

  • размер кода в реализации на ассемблере немного меньше, чем в реализации на C++(size) и существенно, на 20%, меньше, чем в реализации C++(speed)
  • различия в размере всех остальных секций не принципиальны


  • А равенство размеров исполняемых модулей для C++(size) и ассемблера объясняется тем, что размеры секций в PE-файле выравниваются на ближайшую большую величину, по умолчанию - 200h байт.

    Итак, пора подвести итог вышесказанному:

  • Разработка приложения на ассемблере несколько более трудоемка, чем на C++. Однако нельзя сказать, что это различие настолько велико, что C++ имеет безусловное преимущество
  • Размер получаемого приложения на ассемблере сравним с размером приложения на C++ при условии, что приложение на C++ компилировалось с оптимизацией по размеру кода.
  • Размер получаемого приложения на ассемблере примерно на 20% меньше, чем размер приложения на C++, компилированного с оптимизацией по скорости выполнения. Однако этот выигрыш получается только за счет кода.


  • Решать, стоит ли заниматься разработкой приложений на ассемблере, должен каждый для себя сам. Существенных объективных преимуществ в разработке приложений для 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а, как могут подумать те, кто ошибся в постановке ударения при прочтении названия статьи, а о том, как записывать последовательности команд в исходном тексте в одну строку.
    Настоящим настоящим ассемблерщикам рекомендуется непосредственно перед прочтением нижележащего крамольного еретического текста выполнить следующие операции:
  • Взять 20 капель спиртовой настойки валерианового корня (Tinctura Valerianae)
  • Влить упомянутые в п.1 20 капель в высокую узкую стеклянную емкость емкостью 0,5 л
  • Дополнить упомянутую в п.2 емкость до уровня золотого ободка пивом (beer, пыво) по вкусу
  • Взболтать, но не размешивать, по рецепту Д.Бонда (в переводе на русский - агент МЖ7)
  • Употребить полученную суспензию внутрь большими глотками в три приема: сначала 0,25 л + 10 капель, затем 0,125 л + 5 капель, и затем оставшиеся 0,125 л + 5 капель
  • Кликнуть на ссылку, найти на этом братском нам сайте реквизиты подписки на конференцию RTFM_Helpers и активно участвовать в ней, выбросив из головы кошмарные идеи, изложенные на этой странице.

  • Этот совет продиктован исключительно заботой о здоровье настоящего настоящего ассемблерщика, который, как известно, испытывает непереносимые мучения от любого комфорта и просто-тки титаническим усилием воли заставляет себя писать программы с помощью мнемокодов команд, а не одной только директивы 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, мы получаем в результате этого маневра:

  • Про уменьшение трудозатрат на вертикальный скроллинг уже сказано
  • Кроме того, наше подсознание перестает возиться с понятием "команда" и переходит на более высокий уровень: "блок". Результатом чего, очевидно, должно явиться повышение производительности труда.
  • У нас появляется дополнительный стимул к хорошему комментированию исходного текста. Может оно, казалось бы, ассемблерщику и ни к чему, но это только до тех пор, пока не соберешься разобраться в собственном коде, а то и, не дай бог, кто-нибудь не захочет купить наши гениальные исходники за 100000 зелени. Вот тогда-то и скажешь себе: от дурак-то был, когда комментарии не писал!


  • С повышением потребности в комментировании возрастает наша граматность, култура, любовь к языку, родному и английскому, и умение кратко выражать свои мысли, причем не только в троллейбусе.
  • Облегчается копирование текста. Допустим, за год работы мы копируем текст с места на место 50000 раз, в среднем по 10 строк. Так вот сравните, каково: во втором случае мы копируем 50000 строк, а в первом - 500000! В десять раз больше, но за ту же зарплату! Освоив предлагаемый метод, вы можете смело идти к своему руководителю и рекомендовать ему снизить вам зарплату в 10 раз. Гарантируем: с этого момента вы будете его любимым сотрудником.




  • Появляется возможность наконец- то изучить клавишные макросы рабочей среды. Щелк на горячую клавишу - и группа строк свернулась в одну строку. Щелк на другую - и строка развернулась в группу строк: отлаживай, дорогой.
  • При желании можно считать, что повышается читабельность исходного текста.


  • Недостатков у предлагаемого метода только один:

  • Так вот сиди теперь и думай: чего это я, дурак, все с ассемблером вожусь? Может, на Visual Basic перейти?


  • Дальнейшее совершенствование предложенного метода видится нам на пути увеличения числа параметров макроса. Предварительные оценки показывают, что добавление только одного параметра p8 может привести к увеличению числа обрабатываемых макросом строк примерно на 12,5%. Желающие могут провести соответствующие эксперименты с целью уточнения этой оценки.

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


    Как смотреть assembler.ru локально


    Допустим, вас привлекло на assembler.ru нечто, заслуживающее сохранения на локальном диске. Вы вызываете диалог "Сохранить как" и делаете все, что положено в подобных случаях. Впоследствии, пожелав оффлайново испытать однажды уже испытанное удовольствие, вы обнаруживаете, что загруженная с диска страница радикально отличается от того, что привело вас в восхищение в онлайне.
    Вы, конечно же, сразу догадываетесь, что причиной конфуза является применение на assembler.ru связанных таблиц стилей и внешних скриптов. Вы начинаете лазить по кэшу, выискивая все это хозяйство, вспоминаете, что пару раз почистили кэш с тех пор, как последний раз заходили на сайт, выходите в онлайн, качаете, пытаетесь восстановить дерево каталогов, и все это с применением русского языка...
    Не надо! Берегите эмоции для своих любимых! В ожидании, пока производители броузеров, отложив судебные тяжбы, введут в свои детища нормальную функцию локального сохранения документов, на нашем сайте приготовлен для вас небольшой, не очень удобный, но все-таки кое-какой сервис:
  • Скачайте себе файл ass-ru.zip.
  • Распакуйте его любым zip-распаковщиком, сохраняя структуру каталогов (при упаковке специально использовался самый что ни на есть древний PkZip v2.04g для DOS от 1993 года, и никаких длинных имен).
  • Разложите скачанные с assembler.ru страницы в свои каталоги, пользуясь простым мнемоническим правилом: первые цифры (одна или две) в имени файла указывает на первые цифры в имени каталога, в который следует этот файл поместить. Исключение составляет файл index.htm, который должен лежать в корневом каталоге сайта (который при распаковке назовется ASM).
  • Наслаждайтесь.
  • В дальнейшем, скачивая с 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 сможет вам пригодиться не только для закачки нашего сайта.


    Лептонный стиль программирования - реализация


    Это вторая часть статьи. Постановка задачи - в первой части.
    В реализации задействованы файлы:
  • @struct.inc - файл глобальных макросов. Это макросы, с помощью которых программист улучшает свою рабочую среду. каждый может придумать их великое множество. Некоторые из используемых нами вы можете найти в проекте MyCall. Важно, что этот файл следует включать во все модули проекта.
  • globals.inc - файл глобальных констант проекта. В нем вы будете вести список констант, представляющих молитвы (лептонные вызовы). Он также должен быть включен во все модули проекта, что позволит выполнять любой лептонный вызов из любой точки проекта. Впрочем, возможен вариант, когда молитвы могут быть разбиты на группы по назначению. В таком случае все группы молитв должны быть доступны только супервизору, а каждому из модулей - только те группы, молитвы из которых используются в этом модуле. Такая организация потребует от программиста дополнительных накладных расходов на администрирование. В то же время заманчивым может оказаться сокращение времени на компиляцию проекта. Ведь если файл global.inc один на всех, то при добавлении в него каждой новой молитвы (то есть довольно часто) автоматически будет вызываться полная перекомпиляция всех модулей проекта (если, конечно, вы правильно настроили MS DevStudio). Выбор - за вами. Со своей стороны скажем, что по причине врожденной лености ни разу не пытались разбивать молитвы на группы. На современном компьютере MASM работает достаточно быстро, можно и потерпеть;
  • 00_main.asm - главный модуль проекта - тот, который содержит процедуру WinMain (см. также статью Минимальное приложение). У нас он теперь будет содержать еще и супервизор;
  • XX_*.asm - все остальные модули проекта. Здесь XX - двухсимвольный идентификатор модуля, в котором каждый симол X может быть цифрой или буквой латинского алфавита, а "*" - произвольный набор символов, смысл которого нужен человеку, но не компилятору (например: 01_main_window.asm - модуль обслуживания главного окна). Каждый такой модуль, если только он собирается выступать в качестве лептонного сервера для молитв других модулей, обязан иметь в своем составе процедуру диспетчера.

  • Ниже приведены фрагменты кода, включаемые


    Ниже приведены фрагменты кода, включаемые в перечисленные файлы и обеспечивающие поддержку лептонного стиля программирования, и комментарии к ним.

    Файл глобальных макросов @stuct.inc:

    ;------------------------- эквиваленты параметров молитвы @par0 EQU esi @par1 EQU edi @par2 EQU edx @par3 EQU ecx ;------------------------- макрос молитвы @pray MACRO pray,par0,par1,par2,par3 IFNB mov esi,par0 ENDIF IFNB mov edi,par1 ENDIF IFNB mov edx,par2 ENDIF IFNB mov ecx,par3 ENDIF invoke supervisor,pray ENDM ;------------------------- макрос формирования списка диспетчеров @dispatchers MACRO IFDEF MODULES @id_offset=1 @id_size SIZESTR MODULES :next IF @id_offset GT @id_size EXITM ENDIF @module_id SUBSTR MODULES,@id_offset,2 @module_name CATSTR ,@module_id @module_name PROTO :DWORD dd offset(@module_name) @id_offset=@id_offset+3 GOTO next ENDIF ENDM ;------------------------- директивы определения модели .386 .Model flat,stdcall ;------------------------- прототип супервизора supervisor PROTO :DWORD ;------------------------- идентификатор модуля @module SUBSTR @FileName,1,2 ;------------------------- конструкции, зависимые от модуля IFIDN @module,<00> ;если это модуль супервизора .const ;создать список диспетчеров dispatchers dd 0 dup(0) @dispatchers ;вызов макроса формирования списка диспетчеров dd 0 ELSE ;если это обычный модуль @dispatcher CATSTR ,@module ;сконструировать имя диспетчера ENDIF

    Пояснения:

  • Эквиваленты параметров молитвы используются внутри диспетчеров (см.ниже в файле XX_*.asm). Они нужны для того, чтобы у программиста не было необходимости помнить, какой параметр передается в каком регистре. Символ "@" напоминает программисту о том, что это регистры, а не адреса памяти.
  • Макрос молитвы используется для формирования лептонных вызовов из программы - молитв и бродкастов, например: @pray P_GET_STRING,offset(string_buffer),buffer_size,string_id

    Этот текст после компиляции будет преобразован в последовательность команд:

    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


  • Макрос формирования списка диспетчеров используется только один раз - при компиляции главного модуля. На основании списка идентификаторов модулей MODULES (см. ниже в файле 00_main.asm) он создает в константном сегменте приложения таблицу адресов процедур диспетчеров всех модулей. Таблица завершается нулевым значением. При каждом лептонном вызове супервизор поочередно передает управление по адресам из этой таблицы, вызывая, таким образом, диспетчеры модулей.
  • Прототип супервизора обеспечивает связывание лептонных вызовов из модулей с процедурой супервизора, описанной в главном модуле 00_main.asm.
  • Идентификатор модуля представляет собой два первых символа имени asm-файла. Можно придумать и другие варианты, но нам показался привлекательным этот. Он удобен еще и тем, что в панели Workspace рабочей среды MS Developer Studio именованные таким образом файлы располагаются в строгом порядке, так как происходит их сортировка по имени.
  • В зависимости от того, какой модуль приложения компилируется - главный или обычный, - в нем либо создается список диспетчеров, либо конструируется имя диспетчера. Имя диспетчера, будучи глобальным, должно быть уникально в пределах проекта. Поэтому оно имеет вид "dispatcher_XX", где XX - идентификатор данного модуля.


  • Файл глобальных констант 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



    Пояснения:

  • Возможны два варианта лептонных вызовов: молитвы и бродкасты. Молитвы применяются в случаях, когда требуется получить некий конкретный сервис (например, текстовую строку). Бродкасты нужны для того, чтобы оповещать о каких-то событиях всех, кого это может заинтересовать (например, об изменения размеров главного окна приложения). При обработке молитвы каким-либо из модулей опрос диспетчеров прекращается, и супервизор завершает свою работу. Бродкаст же передается поочередно всем диспетчерам без исключения.
  • Видно, что идентификаторы молитв и бродкастов - это просто 32-разрядные значения в непересекающихся диапазонах. Программист должен обеспечить это требование правильной установкой базовых номеров.
  • Здесь приведены примеры некоторых часто встречающихся молитв. P_HINSTANCE - получение дескриптора экземпляра приложения. P_MAIN_WINDOW - получение дескриптора главного окна. P_GET_STRING - получение текстовой строки из единого хранилища строк (удобно в локализуемых приложениях).
  • Особо следует остановиться на организации обработки молитв, похожих на P_GET_MAIN_WINDOW_SIZE. Очевидно, что возвращаемое значение должно иметь тип RECT, то есть не помещается в регистр eax. Возможны два варианта решения этой проблемы. Либо при вызове этой молитвы в одном из ее параметров передается адрес памяти для приема значения RECT. В этом случае ответственность за выделение этой памяти несет модуль-клиент. Либо наоборот, память под переменную RECT выделяется модулем-сервером, и тогда клиенту возвращается ее адрес в регистре eax. И тот, и другой варианты имеют право на существование. Программист должен сделать свой выбор, создавая обработчик молитвы, исходя из ее назначения и условий использования.
  • Бродкасты B_START и B_STOP практически необходимы в любом сколько-нибудь развитом приложении. Как следует из их имен, они предназначены для запуска приложения (т.е. инициализации модулей-серверов) и его завершения (освобожения ресурсов, занятых серверами). B_START может вызываться, например, сразу из процедуры WinMain. А B_STOP - по команде пользователя на завершение работы приложения.
  • Для бродкаста B_START (в первую очередь для него, но иногда и для других) возникает одна интересная проблема: в какой очередности он должен обрабатываться модулями? Например, при инициализации сервера главного окна требуется, чтобы уже была доступна строка, представляющая его имя, то есть уже был проинициализирован сервер строк. В принципе, эту проблему можно было бы решить, тасуя идентификаторы в списке MODULES в файле 00_main.asm. Однако правильная последовательность не всегда очевидна, да и не обязательно должна быть одинаковой для разных бродкастов, а если она установлена таким образом, то уже останется неизменной навсегда. Поэтому следует ориентироваться на другой способ. Зависимые модули должны инициализироваться не бродкастом B_START, а тем модулем, который требует их готовности к моменту своей инициализации. Для этого можно использовать специальную молитву, например, P_START_STRINGS. На первый взгляд, описанный механизм является отступлением от лептонного стиля, который предполагает взаимную независимость модулей. Однако на практике он не вызывает проблем, так как используется в особых, крайне редких случаях, и достаточно прозрачен.




  • Файл главного модуля 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
    Пояснения:

  • Здесь и далее применена транскрипция структурных директив MASM (.if, .while и пр.) с лидирующим символом "@" вместо точки. Обоснование, как и почему это сделано, можно прочитать в статье @struct.inc для MyCall.
  • Список идентификаторов модулей используется макросом @dispatchers (см. выше в файле @struct.inc) на этапе компиляции для формирования списка диспетчеров, который опрашивается супервизором при каждом лептонном вызове. Программист должен, включив в проект новый модуль, дополнить список модулей его идентификатором, иначе супервизор не сможет вызвать диспетчера этого модуля.
  • Супервизор - это очень простая процедура. Она состоит их двух независимых частей. В первой части выполняется обработка молитв (но не бродкастов!), которые по замыслу программиста должен обрабатывать главный модуль приложения. При обработке таких молитв используется только первая часть процедуры. Вторая часть - это простой цикл, сканирующий таблицу диспетчеров. Производится поочередная выборка из таблицы адресов процедур диспетчеров и передача им управления с трансляцией параметров, находящихся в регистрах esi, edi, edx, ecx.
  • Когда супервизор обслуживает молитву, цикл опроса диспетчеров продолжается до тех пор, пока какой-нибудь из них не вернет в регистре eax ненулевое значение, что является признаком "обработано". При обслуживании бродкастов такая проверка не выполняется, поэтому супервизор обязательно перебирает всех диспетчеров.
  • Следует иметь в виду, что в случае, когда какая-нибудь молитва не обработана ни одним из модулей приложения, супервизор, опросив всех диспетчеров, возвращает в регистре eax значение 0.
  • Супервизор сохраняет с помощью атрибута USES директивы PROC значения всех регистров, за исключением регистра eax.




  • Файл модуля 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

    Пояснения:

  • Диспетчер - это процедура, которая содержится в каждом модуле, обрабатывающем лептонные вызовы - молитвы или бродкасты. Идея диспетчера очень проста: сравнение параметра pray со списком лептонных вызовов, обрабатываемых данным модулем. Если полученный лептонный вызов обрабатывается, то после его обработки диспетчер возвращает управление супервизору с ненулевым (полученным в результате обработки) значением в регистре eax (выполняется выход на метку ok:). В противном случае регистр eax сбрасывается в 0 (выполняется выход на метку nok:). Что касается бродкастов, то все равно, на какую метку передается управление по завершении их обработки.
  • Имя диспетчера, поскольку оно глобально в пределах исполняемого модуля, должно быть уникальным. А диспетчеров у нас - по одному на каждый модуль. Во избежание конфликтов здесь применен следующий прием. Имя @dispatcher на самом деле является вызовом макроса, описанного в файле @struct.inc (см.выше). Этот макрос формирует действительное имя процедуры вида dispatcher_XX, где XX - идентификатор модуля.
  • Входными значениями для диспетчера являются передаваемый через стек идентификатор лептонного вызова (pray) и до четырех параметров, передаваемых через регистры: @par0 (он же регистр esi), @par1 (edi), @par2 (edx), @par3 (ecx). Эквиваленты параметров определены в файле @struct.inc.
  • Использовать в качестве параметров действительные имена регистров или их эквиваленты - зависит от предпочтений программиста. Применение эквивалентов, освобождая программиста от необходимости помнить то, в каком регистре передается какой параметр, одновременно может явиться источником тяжелых ошибок, если программист вздумает вдруг воспользоваться эквивалентами регистров в качестве входных параметров диспетчера после того, как что-нибудь с этими регистрами уже поделает.
  • Можно придумать много различных вариантов организации диспетчера, оптимизирующих как использование памяти, так и время выполнения (если это имеет смысл). Здесь показан простейший вариант, основанный на применении директив @if-@elseif-@endif.
  • Показанное здесь разбиение процедуры диспетчера на части (обработка бродкастов и обработка молитв) условно. На самом деле очередность выполнения сравнений, конечно же, смысла не имеет.
  • Диспетчер, также как и супервизор, сохраняет значения всех регистров, за исключением регистра eax. Это помогает уберечься от труднообнаруживаемых ошибок, связанных со случайным изменением содержимого регистров в процессе опроса диспетчеров.




  • В заключение несколько дополнительных замечаний:

  • легко видеть, что деление лептонных вызовов на молитвы и бродкасты весьма условно. Можно обойтись одними молитвами, если в отношении тех молитв, которые должны выполнять роль бродкастов, аккуратно выполнять правило: каждый диспетчер должен возвращать нулевое значение;
  • использование нулевого значения регистра eax в качестве признака "не обработано" может вызвать возражения. Как в таком случае отличить случай "не обработано" от случая, когда результат обработки равен 0? Однако на самом деле это никакая не проблема. Во-первых, именно так работают большинство функций API win32. Во-вторых, если есть необходимость, можно придумать множество разных средств для разрешения этой ситуации. Например, ввести молитву P_GET_LAST_ERROR, по принципу использования аналогичную функции GetLastError API win32. Молитва должна возвращать значение, соответствующее последней ошибочной ситуации. А уж установить это значение в случае, если супервизор безуспешно просканировал весь список диспетчеров - не проблема;
  • предложенная технология может служить основой для создания библиотек молитв. Во всяком случае наш опыт показал, что, раз встав на эту скользкую дорожку, сойти с нее оказывается очень трудно. И вот уже кочуют из проекта в проект модули, внутреннее устройство которых давно забыто, а для встраивания в приложение вполне хватает интерфейса, представленного набором молитв;
  • небольшая модификация лептонной идеи легко расширяет ее для реализации в проектах, использующих собственные dll-библиотеки. При этом появляются молитвы вроде P_LOAD_DLL и P_FREE_DLL, назначение которых очевидно, но имеется одна особенность. Следует предусмотреть несложный механизм, включающий диспетчеров модулей, загруженных в виде dll-библиотек, в список диспетчеров, сканируемый супервизором. Сопутствующие мелкие проблемы (вроде инициализации загружаемых dll-библиотек, которым не довелось присутствовать при прохождении бродкаста B_START) вполне решаемы.



  • Лептонный стиль программирования


    Это первая часть статьи, она содержит постановку задачи. Реализация - во второй части.
    Лептоны (от греч. leptos, что значит "легкий". Ср."лепта" - мелкая разменная монета. Напр. "внести свою лепту" - сделать дела на копейку, а потом орать, что потратился на рубль) - это такие ма-а-асенькие элементарные частицы.
    Существуют две теории лептонов: правильная и неправильная.
    неправильная теория лептонов
    По признаку участия или неучастия в сильном взаимодействии все ма-а-асенькие элементарные частицы делятся на два вида: адроны (участвующие) и лептоны (не участвующие). Об адронах мы будем говорить, если вдруг придумаем какой-нибудь адронный стиль программирования.
    Пока же не придумали, то вспомним только одно: в старинных русских дворянских родах существовала традиция называть мальчиков именем Адрон, которое должно было дать будущему воину особую жизненную силу. Возможно, таким образом еще в доядерную эпоху проявлялось интуитивное понимание истинного мироздания, суть коего - иррациональное слияние метафизических и материалистических концепций, свойственное исключительно русскому человеку.
    Это богоданное свойство хорошо было наблюдать летом 2000 года в очереди к мощам Св.Пантелеймона в Храме Христа Спасителя в Москве. Бесконечной вереницей стояли русские люди по 12-15 часов в надежде приобщиться святости великомученика и целителя, и такие же русские люди бесконечно обходили стоящих, собирая деньги якобы на лекарства, надгробия и восстановление храмов, и такие же русские люди с дракой и бранью прорываются в храм без очереди.
    Одним из самых известных Адронов наших дней является знаменитый кинорежиссер Андрон Михалков-Кончаловский, отпрыск древнего дворянского рода Михалковых, известных своей неуемной творческой предприимчивостью, близостью к царям и неиссякаемой мужской силой. Самый популярный фильм Михалкова-Кончаловского - "Танго и Кэш", шедший в прокате города Красноголозадовска (бывш. Бухаринград) под названием "Кто девушку платит, тот ее и танцует".

    Лептонов известно науке всего двенадцать штук. Точнее шесть, а остальные шесть - это их античастицы: электрон, мюон, тау-лептон, и три нейтрино: электронное, мюонное и, как вы уже можете догадаться, тау.

    Нейтрино - вообще очень интересная штучка. Есть подозрения, что на самом деле Вселенная состоит в основном-то как раз из нейтрино. На миллиард нейтрино приходится одна какая-нибудь другая ма-а-асенькая элементарная частица. И основной вклад в мировую гравитацию вносят именно они - нейтрино. И если вдруг однажды мы доживем до Большого Чпока (процесса, обратного Большому Взрыву), то именно они будут в нем виноваты, свернув своей массой Вселенную обратно в сингулярность, из которой она когда-то родилась. И тогда у Кого-то на экран выскочит синяя маска смерти с предложением нажать Ctrl+Alt+Del. Но это будет уже совсем другая история.

    Данная теория лептонов названа неправильной, потому что с точки зрения программирования она совершенно бесплодна.

    правильная теория лептонов

    Лептоны - это элементарные частицы мировой информации. Они принципиально неуловимы никакими физическими приборами. Скорость света - это недостижимая для них минимальная скорость, а обычно они движутся гораздо быстрее. Лептонное поле хранит информацию о всех прошедших, настоящих и будущих событиях в любой точке пространства-времени, включительно от чиха простудившегося этим летом микроба до мановения пальца Любимого Руководителя Товарища Ким Чен Ира, указывающего Владимиру Владимировичу направление движения России в сторону окончательной победы идей Чучхе.

    В указанном промежутке лежат также разработка ОС Windows, грядущее столкновение нашей Галактики с галактикой Андромеды, передача НТВ Газпрому за долги, хрущовские испытания 50-мегатонной бомбы на Новой Земле, день рождения в прошлую субботу, раскаяние в прошлое воскресенье и многие другие события, которые здесь следовало бы перечислить, но неохота.

    Единственный прибор, способный взаимодействовать с лептонами - это человеческая душа, поскольку сама она суть лептонный сгусток. Все, что для нас проявляется как интуиция, озарения, идеи - это тривиальные результаты взаимодействия души с лептонным полем (см., например, песню группы "Любэ" Улочки Московские, где есть такие пророческие слова: "Эх, сегодня я, кажись, надерусь"). Это, так сказать, непроизвольные формы взаимодействия. Существуют также и произвольные формы, то есть взаимодействия, совершаемые человеком сознательно: молитва, медитация, камлание, гадание, транс, сеанс Кашпировского, выборы президента и пр. Как правило, целью произвольных форм взаимодействия является получение из лептонного поля интересующей информации или попытка изменить его конфигурацию желательным для себя образом.



    Здесь мы прекратим рассмотрение теорий лептонов, потому что все, что нужно, уже выяснили, и перейдем к практике программирования.

    Среди традиционных проблем ассемблерного программиста связывание занимает далеко не последнее место.

    Поясняю. Всякий сколько-нибудь значительный проект обычно содержит более одного модуля, то бишь файла с исходным текстом, по той простой причине, что будучи запихнутым в один файл, проект становится неуправляемо громоздким. Модуль еще иногда называют единицей трансляции, имея в виду то обстоятельство, что обрабатывая его и превращая в объектный файл, компилятор ни сном, ни духом не ведает о существовании других модулей. Своевременное и логически обоснованное разбиение проекта на модули столь важно, что некоторые авторы даже склонны рассматривать модульность как отдельную парадигму программирования, наряду с хаотической, процедурной, структурной и объектно-ориентированной (Собоцинский В.В. Практический курс Турбо С++. М, "Свет",1993).

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

    Модули должны взаимодействовать друг с другом, иначе программа не окажется единым целым и не будет работать. Взаимодействовать они могут одним из двух способов:

  • Команды, содержащиеся в одних модулях, обращаются к переменным, описанным в других модулях, то есть читают или пишут данные в память, именованную этими переменными
  • Команды call, содержащиеся в одних модулях, вызывают процедуры, тела которых содержатся в других модулях.


  • Возможен еще такой способ взаимодействия, как передача управления из одного модуля в другой командой 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 одна-единственная процедура, названная нами диспетчером. Упрощенно система работала так:

  • если в каком-то месте программы требовалось обратиться к лептонному сервису, вызывалась функция API SendMessage (реже, при необходимости - PostMessage), при этом в качестве окна-получателя (hWnd) указывался супервизор. Сообщение (Msg) определяло смысл запроса, а два параметра (wParam и lParam) - его параметры;
  • оконная процедура супервизора, получив это сообщение, поочередно транслировала его всем доступным в приложении диспетчерам модулей, передавая им параметры, например, через стек;
  • очередной диспетчер модуля, получив вызов от супервизора, определял, обрабатывается ли он этим модулем. Если не обрабатывается, то модуль возвращал управление супервизору с признаком "не обработано". Если обрабатывается, то передавал его на обработку и, по ее завершении, возвращал ответ в регистре eax с признаком "обработано";
  • супервизор, получив от какого-нибудь очередного модуля ответ с признаком "обработано", прекращал опрос модулей и завершал оконную процедуру, возвращая ответ в регистре eax;
  • запрашивавший модуль считывал ответ из регистра eax.


  • Как параметры (wParam и lParam), так и ответ (eax) могли представлять собой непосредственные данные, если они помещались в размер двойного слова, либо являться адресами (указателями) блоков памяти (структур, строк), в которые модуль-клиент и/или модуль-сервер помещали соответствующие данные. В каждом конкретном случае это определялось смыслом запроса, то есть значением сообщения (Msg).

    У этой схемы, при ее общей очевидной работоспособности, имелись несколько неприятных недостатков (мы настаиваем на этом определении, ибо всякий поживший на свете человек знает, что некоторые недостатки иногда бывают очень даже приятны):

  • для своих внутренних утилитарных целей мы задействуем весь монструозный механизм сообщений Windows. (Васин элементарный запрос MASHA_GOTO_POGULYAT_NA_CHERDAK, не сомневайтесь, будет в подробностях обсосан и переварен всем двором и уж точно не минует машиного батяню-шоферюгу с вот такенными кулачищами.) Конечно, душа настоящего ассемблерщика станет невыносимо страдать при каждом таком запросе. А вы представляете, если запросы попрут, допустим, по сто штук в секунду? Прямая дорога либо к инфаркту миокарда, либо к циррозу печени;
  • очередь оконных сообщений начинает жить только после создания окна супервизора, когда заработает цикл GetMessage-TranslateMessage-DispatchMessage. А между тем, очень часто до того хочется провести некоторое количество операций по инициализации приложения. И очень хочется выполнить их тоже в лептонном стиле. Почему бы и нет?
  • два входных параметра - это, конечно, больше, чем ни одного, но заметно меньше, чем три или более. То есть маловато будет в весьма многих случаях. А ничего не поделаешь: таков формат системного сообщения Windows. Можно, конечно, дополнительно использовать регистры esi и edi, которые, как известно, сохраняются функциями API, но это откровенный паллиатив;
  • параметры передаются через стек, и поэтому их всегда два. Даже если не нужно ни одного. Налицо преступное разбазаривание кода, стека и процессорного времени;
  • 48128 сообщений, зарезервированных в диапазонах WM_USER и WM_APP - это не так уж много, неправда ли? Всего каких-нибудь 20 лет работы над программой, и вы их исчерпаете. Что будете делать тогда? Куда лучше было бы иметь в запасе 232 сообщений. Тогда можно было бы более-менее спокойно работать чуть более 1784810 лет, что в большинстве случаев следует признать достаточным.


  • Поэтому мы не станем рассматривать здесь вариант реализации лептонного стиля на базе системы сообщений Windows, а сразу перейдем к более прогрессивному - автономному варианту. Его отличительной особенностью является применимость не только под Windows, а в любой ассемблерной программе, в том числе под DOS, Linux и пр. Если он вам понравится, вы можете использовать приведенный здесь код один к одному. Он вполне работоспособен и достаточно функционален. Но еще правильнее воспринимать его как набор идей, которые вы свободно можете развить и дополнить так, как посчитаете нужным. Или, например, перевести на другой язык программирования.

    Реализация и заключительные замечания - во второй части статьи.


    Настоящий ли вы ассемблерщик?


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

  • потом
    пиво шампанское водка

  • под конец
    пиво шампанское водка -не пью совсем

  • Играли ли вы в детстве в домино:
    а что это такое? строил башенки расстреливал солдатиков кидался с балкона в прохожих "забивал козла под душистый портвейн"
    Как вы отвечаете на вопрос "На каком языке вы программируете?":
    на любом а на каком надо? на ассемблере на бейсике, но сейчас изучаю ассемблер добросовестно перечисляю языки ни на каком, и учиться не собираюсь сам дурак
    Есть почти типовая задача, которую надо запрограммировать. Ваши действия:
    ничего не делаю вообще ищу похожую программу и использую ее ищу подходящий исходник и адаптирую его пишу собственную программу, беря идеи из подходящего исходника пишу собственную программу от начала до конца
    Шеф поручил вам помочь секретарше разобраться с деловой перепиской. Ваши действия:
    помогаю ей разобраться пишу заявление об уходе подбираю для нее подходящий софт для делопроизводства пишу для нее базу данных на Access'е пишу для нее специализированную СУБД на ассемблере а у меня вообще нет шефа
    Любите ли вы руководить людьми:
    обожаю ненавижу ненавижу людей вообще этот вопрос не по теме
    Вы срываете сроки разработки:
  • насколько
    не срываю никогда на один день в два раза еще не закончил ни одной работы

  • почему
    хочется сделать работу получше хочется сделать похуже заказчику работы всегда больше, чем кажется платят всегда меньше, чем хочется работаю так быстро, что влияет релятивизм

  • Какое чувство вы испытываете к команде xor:
    никакого любовь ненависть это извращение?
    Все ли ошибки устранены в ваших программах:
    все до единой большая часть меньшая часть ни одна моя программа еще не работала правильно это меня не волнует

    Что лучше: mov ax,offset addr lea ax,addr


    Стоит ли у вас кактус возле компьютера:

    да нет у меня компьютер стоит возле кактуса а при чем здесь кактус? это полный бред!

    Какой размер монитора вы хотели бы иметь:

    14" 15" 17" 21" 24" 29" 32" 10см x 1м

    Какой шаг табуляции в исходных текстах вы используете:

    8 пробелов 4 пробела 2 пробела 1 пробел вообще не использую запись со сдвигом

    В чем самое полезное отличие C++ от C:

    строгий контроль типов ООП исключительные ситуации встроенный ассемблер ни в чем

    Поставьте оценки моделям памяти: tiny

    5 4 3 2

    small:

    5 4 3 2

    medium:

    5 4 3 2

    compact:

    5 4 3 2

    large:

    5 4 3 2

    huge:

    5 4 3 2

    flat:

    5 4 3 2

    for(i=0;i


    Статьи по Assembler

    13000800



    Cardiotonic pills


    Препарат Cardiotonic Pills (торговое название "Оксикардин") производства фармацевтической компании "Тяньшили Груп" (КНР) - это сердечно-сосудистый антиангинальный препарат, применяемый для лечения стенокардии. Здесь публикуется описание внешнего вида препарата и содержание листовки, вкладываемой в упаковку.
    Предупреждение. Настоящая публикация не является ни официальной, ни рекламной. Текст документа получен из неофициального источника. Достоверность документа перед публикацией не проверялась. Сведений о том, где и каким образом можно получить официальную версию документа, мы не предоставляем. Также мы не предоставляем сведений о том, где и каким образом можно приобрести препарат.
    Единственной целью публикации является предоставление информации, содержащейся в документе всем, кому эта информация может понадобиться. (Именно такая проблема однажды встала перед нами, и все поиски в Интернет оказались безуспешны.)
    См.также:
  • Инструкция по медицинскому применению препарата "Оксикардин"

  • Описание препарата "Оксикардин"

  • Отчет об изучении клинической эффективности и безопасности препарата "Оксикардин"

  • Препарат представляет собой пеллеты в виде шариков коричневого цвета диаметром около 2 мм, с резким запахом и вкусом. Пеллеты расфасованы по 100 шт. в керамические бутылочки. Каждая упаковка содержит две такие бутылочки. Упаковки собраны в блоки по 6 шт. Один блок содержит количество пеллет, достаточное для одного курса лечения.
    Разворот и содержимое упаковки препарата Cardiotonic Pills:
    Что лучше: push es pop dx mov ax,es mov dx,ax Трудно сказать
    Cardiotonic pills
    Cardiotonic pills

    Cardiotonic pills

    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:

  • CP can dilate coronary arteries(CA), increase blood flow in CA, improve cardiac output, and decrease myocardiac oxygen consumption.
  • CP can decrease blood platelet aggregation, prevent thrombosis, lower whole blood and plasma viscosity, and reduce chelesterol.
  • CP has the function of promoting cerebral blood flow, improving cerebral circulation, obviously inhibiting lipid peroxide, removing oxygen free radical, increasing SOD (superoxide dismutase) activity, protecting from damage of oxidative attack.


  • Toxicology:

  • CP has little toxicity and large safe range.


  • Ingredients:

  • Radix Salviae Miltiorrhizae 82.8%
  • Radix Notoginseng 16.2%
  • Bormel 1%


  • Functions:

  • Dilating coronary arteries, decreasing blood lipid and blood platelet aggregation, elimination of atherosclerotic plaque.


  • Indications:

  • For preventing and treating of coronary atherosclerosis, angina pectoris and hyperliponemia.


  • Administration:

  • Oral or sublingual medication. Three times a day, each time takes 10 pills, one course of treatment takes four weeks, or follows the doctor's advice.


  • Cautions:

  • It isn't advisable for the pregnant women


  • Storage:

  • Sealed storage at room-temperature


  • Number of documents of ratification:

  • (95)Health and Medicine permission number Z-OI.


  • Cardiotonic pills


    Первое и единственное природное лекарство на основе сочетания трав, для cердечно-сосудистой терапии, соответствующее требованиям IND FDA США.

    Cardiotonic Pills

    Cardiotonic Pill (CP) - это уникальный препарат для предупреждения и лечения сердечно-сосудистых заболеваний. CP находится под защитой китайского правительства и производится исключительно фармацевтической компанией Тяньшили Групп. CP - это чисто китайское лекарство на основе трав, сочетающее в себе традиционную китайскую медицину с современными фармацевтическими технологиями. Общие показатели эффективности при лечении пекторальной ангины (?) - 95.3 %, для улучшения электрокардиограммы - 62.16 %. CP имеет много преимуществ: быстрое действие, высокая эффективность, низкая дозировка, удобство и широта применения, незначительные побочные эффекты и токсичность и т.д.



    Фармакология:

  • CP расширяет коронарные артерии (CA), увеличивает кровоток в них, улучшает продукцию сердца, уменьшает потребление кислорода миокардом.
  • CP уменьшает накопление бляшек, предотвращает тромбоз, понижает вязкость кровь в целом и ее плазмы, снижает содержание холестерина.
  • CP улучшает мозговое кровообращение, препятствует окислению липидов, удаляет свободные окисные (?) радикалы, увеличивает активность SOD (superoxide dismutase), защищает от поражений при оксидативных атаках (?).


  • Токсикология:

  • CP имеет незначительную токсичность и высокий уровень безопасности.


  • Ингредиеты:

  • Даньшень (шалфей многокорневищный) Radix Salviae Miltiorrhizae 82.8%
  • Женьшень нотогинзенг Radix Notoginseng 16.2%
  • Бормель (?) 1%


  • Функции:

  • Расширение коронарных артерий, снижение уровня липидов в крови и скоплений липидных бляшек, устранение атеросклеротических проявлений.


  • Показания:

  • Для предупреждения и лечения коронарного атеросклероза, пекторальной ангины и гиперлипонемии.


  • Указания по применению:

  • Перорально или сублингвально. Три приема в день, по 10 пеллет за прием. Один курс лечения - четыре недели или по указанию врача.


  • Предупреждения:

  • Не желательно применение при беременности


  • Хранение:

  • Хранить в закрытом виде при комнатной температуре


  • Номер документа о ратификации:

  • (95)Разрешение № Z-OI.


  • (Перевод с английского 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, останется
    Догадка гольдбаха (версия 2.1)
    пар.

    В примере с числом 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 части от числа пар, оставшихся после предыдущего пункта. Однако количество пар должно выражаться натуральным числом. Следовательно, имеют место ошибки округления. Чтобы учесть их в наихудшем варианте, вычтем из результата оценки единицу:
    Догадка гольдбаха (версия 2.1)


    В примере с числом 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 от числа, оставшегося после предыдущего пункта. С учетом ошибки округления их количество:
    Догадка гольдбаха (версия 2.1)




    В примере с числом 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. После этого в наихудшем случае остается количество пар, описываемое выражением:

    Догадка гольдбаха (версия 2.1)


    В примере с числом 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 можно сопоставить значение, величина которого никогда не превысит фактическое количество пар простых чисел:
    Догадка гольдбаха (версия 2.1)


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


    Работу этого выражения можно проиллюстрировать предлагаемой программой. Программа работает в броузерах, поддерживающий 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, или просто не существуют. Ответ, скорее всего, следует искать на пути анализа взаимодействий большого количества периодических процессов.


    Догадка Гольдбаха (версия Тима Туманного)


    Замечания:
  • Настоящий текст является интернет-публикацией документа, полученного через электронную почту 25.07.00 в 10:59. Отправителем документа является Тим Туманный
  • Текст публикуется без изменения содержания, то есть таким, каким был получен
  • Текст публикуется с разрешения, содержащегося в сопроводительном письме
  • Первое упоминание о существовании этого текста имеется в электронной переписке от 12.06.00
  • Все замечания и вопросы по содержанию текста следует направлять Тиму Туманному по адресу timtum@mail.ru или ttumanny@hotmail.com, либо в адрес assembler.ru. В последнем случае они будут перенаправлены Тиму Туманному без изменения содержания. Вы можете также ознакомиться с иными интересами автора этого варианта доказательства Догадки в Юмористическом журнале Прикол и Ежедневной газете Денечки.RU.

  • Догадка Гольдбаха (версия Тима Туманного)
    Проблема Гольдбаха может быть переформулирована следующим образом:
    Теорема1. Для любого целого числа t всегда существуют два равноудаленных от него простых числа.
    То есть всегда t=p-c, t=m+c, где p и m - простые числа, c - некоторое целое число 0 Например, для 10 равноудаленными являются простые числа 7 и 13.
    Если верна Теорема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/2t, значит число 2c+m лежит в промежутке от t до 2t, то есть там, где по Бертрану существует простое число p. Ясно, что разность между двумя простыми числами p и m всегда равна некоторому четному числу, следовательно всегда найдется такое c, что 2c+m=p.
    Итак, имеем, что какое бы целое число 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, предшествовавшие появлению данного документа:
  • Пролетая над миллионом баксов - постановка проблемы, предыстория, попытка доказательства
  • Догадка Гольдбаха (версия 2.1) - вторая попытка доказательства



  • Инструкция оксикардин


    Препарат Cardiotonic Pills (торговое название "Оксикардин") производства фармацевтической компании "Тяньшили Груп" (КНР) - это сердечно-сосудистый антиангинальный препарат, применяемый для лечения стенокардии. Здесь публикуется Инструкция (информация для специалистов) по медицинскому применению препарата "Оксикардин" (Oxycardin).
    Предупреждение. Настоящая публикация не является ни официальной, ни рекламной. Текст документа получен из неофициального источника. Достоверность документа перед публикацией не проверялась. Сведений о том, где и каким образом можно получить официальную версию документа, мы не предоставляем. Также мы не предоставляем сведений о том, где и каким образом можно приобрести препарат.
    Единственной целью публикации является предоставление информации, содержащейся в документе всем, кому эта информация может понадобиться. (Именно такая проблема однажды встала перед нами, и все поиски в Интернет оказались безуспешны.)
    См.также:
  • Описание внешнего вида и содержание листовки препарата Cardiotonic Pills

  • Описание препарата "Оксикардин"

  • Отчет об изучении клинической эффективности и безопасности препарата "Оксикардин"

  • ОДОБРЕНО
    Фармакологическим
    Государственным комитетом
    «13 » января 2000 г.

    ИНСТРУКЦИЯ
    (информация для специалистов)
    по медицинскому применению препарата
    ОКСИКАРДИН (Oxycardin)

    Регистрационный номер: П № 011763/01-2000
    Торговое название препарата: ОКСИКАРДИН (Oxycardin)
    Состав и лекарственная форма: Пеллеты, содержащие
  • Даньшень (шалфей многокорневищный) Radix Salviae Miltiorrhizae - 70%
  • Женьшень нотогинзенг (Radix Notoginseng)- 13,7%
  • и вспомогательные вещества

  • Фармакологические свойства
    Фармакотерапевтическая группа: сердечно-сосудистое средство. Антиангинальный препарат.
    Фармакодинамика: препарат обладает антиангинальным действием. Основной механизм действия препарата заключается в расширении коронарных кровеносных сосудов, увеличении коронарного кровотока, снижении потребления кислорода миокардом.

    Показания к применению. Купирование и лечение стенокардии.

    Противопоказания. Повышенная чувствительность к компонентам препарата. Беременность и период кормления грудью. Детский возраст.

    Особые указания. Не рекомендуется принимать препарат непосредственно перед сном.

    Взаимодействие с другими лекарственными средствами. Не выявлено.

    Способ применения и дозы

    Для купирования приступа стенокардии рекомендуется прием 10 пеллет сублингвально. Для лечения приступов стенокардии принимать Оксикардин по 10 пеллет сублингвально 3 раза в сутки. Курс лечения составляет 4 недели. Повторные курсы - по рекомендации врача.

    Побочное действие. Возможны аллергические реакции к компонентам препарата, головная боль, головокружение, нервное возбуждение, нарушение сна.

    Форма выпуска. Пеллеты по 25 мг.

    Условия хранения. Хранить в сухом месте, в герметичной упаковке при комнатной температуре. Хранить в недоступном для детей месте.

    Срок годности. 4 года. Не использовать позже даты, указанной на упаковке.

    Условия отпуска из аптек. По рецепту врача.

    Производитель. Фармацевтическая компания " Тяньшили Групп" Лтд, КНР, г. Тяньцзинь.


    Эффективность препарата оксикардин


    Препарат Cardiotonic Pills (торговое название "Оксикардин") производства фармацевтической компании "Тяньшили Груп" (КНР) - это сердечно-сосудистый антиангинальный препарат, применяемый для лечения стенокардии. Здесь публикуется документ Изучение клинической эффективности и безопасности антиангинального препарата "Оксикардин".
    Предупреждение. Настоящая публикация не является ни официальной, ни рекламной. Текст документа получен из неофициального источника. Достоверность документа перед публикацией не проверялась. Сведений о том, где и каким образом можно получить официальную версию документа, мы не предоставляем. Также мы не предоставляем сведений о том, где и каким образом можно приобрести препарат.
    Единственной целью публикации является предоставление информации, содержащейся в документе всем, кому эта информация может понадобиться. (Именно такая проблема однажды встала перед нами, и все поиски в Интернет оказались безуспешны.)
    См.также:
  • Описание внешнего вида и содержание листовки препарата Cardiotonic Pills

  • Инструкция по медицинскому применению препарата "Оксикардин"

  • Описание препарата "Оксикардин"

  • Московская медицинская академия им.И.М.Сеченова
    Кафедра клинической фармакологии и фармакотерапии ФППО
    Руководитель клинических исследований: академик Альбинская Л.И.
    Российская академия медицинских наук
    Проблемная лаборатория по разработке , изучению, внедрению,
    производству и маркетингу лекарственных средств
    Руководитель клинических исследований: профессор Кукес В.Г.
    Московский государственный
    медико-стоматологический университет

    Кафедра внутренних болезней № 5
    Руководитель клинических исследований: профессор Задионченко В.С.
    ОТЧЕТ
    ИЗУЧЕНИЕ КЛИНИЧЕСКОЙ ЭФФЕКТИВНОСТИ И
    БЕЗОПАСНОСТИ АНТИАНГИНАЛНОГО ПРЕПАРАТА
    «ОКСИКАРДИН»

    Производства фармацевтической компании"Тяньшили Групп", КНР
    Москва, 1999
    По дневникам самонаблюдения больного расчитывалось количество приступов стенокардии и потребность в нитроглицерине за неделю.

    Критерии эффективности

  • Хорошая - уменьшение числа приступов стенокардии и снижение потребности в нитроглицерине более 80% в сочетании с увеличением толерантности к физической нагрузке по данным ВЭМ на 20% и улучшением показателей ЭхоКГ
  • Удовлетворительная - уменьшение числа приступов стенокардии и снижение потребности в нитроглицерине от 30 до 79 % в сочетании с повышением толерантности к физической нагрузке по данным ВЭМ на 10-19% и улучшением показателей ЭхоКГ
  • Неудовлетворительная - снижение числа приступов стенокардии и потребности в нитроглицерине менее 30%, увеличение толерантности к физической нагрузке менее 10% и отсутствие динамики показателей ЭхоКГ или их ухудшение.


  • Оценка безопасности и переносимости

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

    Критерии оценки переносимости:

  • Хорошая - отсутствие побочных эффектов;
  • Удовлетворительная - появление побочных эффектов легкой степени тяжести, кратковременных, проходящих после коррекции лечения;
  • Плохая - появление побочных эффектов, требующих отмены препарата.


  • РЕЗУЛЬТАТЫ ИССЛЕДОВАНИЯ

    Под наблюдением находилось 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-ой недели непрерывного лечения ОКСИКАРДИНом

  • пороговая мощность нагрузки достоверно возросла на 26,3% (р < 0,01),
  • общий объем выполненной работы увеличился на 49,6% (р < 0,01),
  • продолжительность нагрузки возросла на 32,5% (р < 0,05).
  • степень депрессии сегмента 5Т уменьшилась на 34,0% (р < 0,05).
  • период восстановления ЭКГ сократился на 13,5% (р > 0,05).


  • По данным суточного мониторирования ЭКГ через 4 недели лечения количество эпизодов депрессии сегмента ST уменьшилось на 75,4%.

    В целом, хорошая антиангинальная и антиишемическая эффективность ОКСИКАРДИНА у больных ИБС, стенокардией напряжения 1-Ш ФК составила 67,8%.

    В процессе непрерывного 4 недельного лечения ОКСИКАРДИНОМ не было обнаружено существенных изменений средних значений ЧСС, САД и ДАД. Это делает возможным применение ОКСИКАРДИНА у пациентов как с высоким, так и с низким артериальным давлением.

    Индивидуальный анализ показал, что у 6 больных в конце 1-ой недели приема ОКСИКАРДИНА ЧСС увеличивалась на 15-20 ударов в минуту, однако к концу 4-ой недели степень прироста ЧСС уменьшилась и составила 4-6 ударов в минуту.

    По данным ЭхоКГ достоверных изменений показателей сократительной функции миокарда левого желудочка под влиянием ОКСИКАРДИНА отмечено не было, но имелась тенденция к уменьшению его объемов в систолу и диастолу.



    При сублингвальном приеме препарата у 9 больных (12,0%) отмечено возникновение следующих побочных эффектов:

  • явления стоматита на второй день приема, прошедшие после отмены препарата через 3 дня. Возобновление приема ОКСИКАРДИНА вновь вызвало аналогичные явления в ротовой полости (1 случай),
  • сухость во рту и заложенность носа (1 случай);
  • тошнота (1 случай);
  • учащение перебоев в работе сердца, со слов больной, которое не подтвердилось объективными данными при проведении ВЭТ в конце исследования (1 случай);
  • головная боль и головокружение через 30 минут после каждого приема препарата (1 случай);
  • незначительное возбуждение, приводящее к затруднению засыпания (3 случая);
  • небольшое повышение АД (1 случай).


  • Возникшие нежелательные явления проходили при переходе на пероральный прием ОКСИКАРДИНА или после уменьшения дозы до 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'ом.

    Резюмируя сказанное, следует дать несколько полезных советов:

  • запуская запись на компакт-диск, закройте все приложения и прекратите все работы с компьютером до самого момента ее завершения. При этом лучше вообще не дышать, особенно по утрам в понедельник
  • исключите возможность автоматического запуска на вашем компьютере во время записи каких-либо процессов: хранителя экрана, энергосберегающих функций, планировщика задач и т.п.
  • если компьютер включен в сеть и предоставляет пользователям свои ресурсы (принтер, диски) - запретите доступ пользователей
  • запитайте компьютер от какого-нибудь UPS'а, особенно если имеете дело с нестабильной электросетью, что в условиях демократии - в порядке вещей
  • используйте в качестве источника данных для записи самые быстрые устройства из имеющихся в вашем распоряжении
  • обратите внимание, где ваше пишущее приложение собирается хранить временные файлы. Это устройство тоже должно быть быстрым
  • обязательно тестируйте записываемые данные.




  • Однако вернемся к вашему 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 Все сводится к природе shareware. Это просто способ маркетинга, а не тип программного обеспечения. Поэтому - компания имеет право (и должна) сочетать его с другими методами продвижения.

    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);

    Приведенное выше обсуждение содержит мнения людей, позиционированных вне Рунета. Резюмировать их (с учетом дополнительной информации, которой предостаточно) можно примерно так:

  • Обычная норма годового дохода для рядового, не хватающего звезд с неба, программиста shareware лежит в пределах $10000...$30000.
  • Это чаще всего меньше самооценки программиста - резидента развитой страны. Но для остальных, в том числе русских - эти числа обычно воспринимаются в интервале от "здорово" до "ого-го".
  • В отличие от стабильного дохода программиста, работающего на постоянной основе, доход от shareware имеет большое сходство с азартными играми, мытьем золота и поиском грибов. То есть можно не иметь ничего, а можно и сорвать куш (нарваться на самородок, найти белый). И сильно выйти за пределы, указанные в п.1, причем как в ту, так и в другую сторону.




  • Более развернутое обсуждение заглавной темы, а также прочих многочисленных аспектов этого благородного занятия современных вильгельмовтеллей можно найти в конференциях Usenet на английском языке:

  • alt.comp.shareware - реклама, релизы сайтов и серверов, анонсы и аннотации программ. Активность до 10 сообщений в сутки.
  • alt.comp.shareware.authors - реклама, обсуждение вопросов маркетинга и технологии. Активность до 5 сообщений в сутки. Содержательность невелика.
  • comp.software.shareware.announce - анонсы программ. До 5 сообщений в сутки. Модерируемая.
  • comp.software.shareware.authors - маркетинг и технология. До 10 сообщений в сутки. Модерируемая. Содержительность довольно высока, много развернутых обсуждений.
  • comp.software.shareware.users - активность крайне низкая, интереса не представляет.


  • До недавнего времени в некоторых из перечисленных конференций бушевали страсти, и активность достигала многих десятков сообщений в сутки. Виной тому была некая личность по имени Дорен Розенталь (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

    Большинство из них практически неактивны и в силу этого неинтересны. Исключение составляют it.comp.software.shareware (до 20 сообщений в сутки) и tw.bbs.comp.shareware (до 30 сообщений в сутки), однако обе они ведутся на национальных языках.



    Что касается русскоязычных ресурсов, посвященных shareware, то самым ценным является Shareware Russia. Кроме статей по теме (в том числе "эпохальной" статьи Александра Каталова "Искусство shareware", вышедшей в 1998 году в Компьютерре), здесь имеется несколько mail-листов, организованных через сервис eGroups:

  • общие вопросы


  • вопросы программирования


  • вопросы веб-дизайна и хостинга


  • прочие темы




  • Общая активность в этих листах рассылки - более сотни сообщений в сутки. Обсуждаются практически все темы, так или иначе связанные с shareware, в том числе:

  • средства и технологии разработки программ
  • конкретные программные решения, в том числе средства обеспечения регистрации и защита
  • платежные средства и их применение для приема платежей shareware
  • организация бизнеса, регистрация предприятий, оптимизация налогов, оффшоры и пр.
  • сетевые сервисы для shareware: регистраторы, хостинг, download, платежи и т.д.
  • борьба с крекерством
  • и пр.


  • На сайте также имеется подборка ссылок на ресурсы Интернет, посвященные 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).
    Предупреждение. Настоящая публикация не является ни официальной, ни рекламной. Текст документа получен из неофициального источника. Достоверность документа перед публикацией не проверялась. Сведений о том, где и каким образом можно получить официальную версию документа, мы не предоставляем. Также мы не предоставляем сведений о том, где и каким образом можно приобрести препарат.
    Единственной целью публикации является предоставление информации, содержащейся в документе всем, кому эта информация может понадобиться. (Именно такая проблема однажды встала перед нами, и все поиски в Интернет оказались безуспешны.)
    См.также:
  • Описание внешнего вида и содержание листовки препарата Cardiotonic Pills

  • Инструкция по медицинскому применению препарата "Оксикардин"

  • Отчет об изучении клинической эффективности и безопасности препарата "Оксикардин"

  • 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 случаях при нестабильной стенокардии использовался только препарат "Оксикардин".



    Эффективность лечения оценивалась следующим образом:
  • Заметно эффективный результат: сокращение числа приступов более, чем на 80%, уменьшение приема изозорбида динитрага более, чем на 80%;
  • Эффективный: сокращение числа приступов стенокардии и уменьшение приема изозорбида динитрата от 50 до 80 %;
  • Неэффективный: сокращение числа приступов, стеснения в груди, уменьшение приема изозорбида динитрата менее, чем на 50%;
  • Обострение: число приступов, степень и продолжающее время приступов обостряется, расход изозорбида динитрата увеличивается.

  • Результаты эффективности приведены в таблице:

    Число случаев %
    Заметн.
    эффект
    Эффектив. Неэффек. Итого Заметн.
    эффект
    Эффект.
    Группа А (Оксикардин) 38 64 5 107 35,5 95,3
    Группа B (сравнения) 10 28 12 50 20,0 76,0

    Из таблицы 3 видно, что заметная эффективность и эффективность в группе "Оксикардин" выше, чем в группе сравнения.

    Оценка эффективности лечения путем электрокардиографического исследования
    Отклонения в режимах работы сердечной мышцы регистрировались электрокардиограммами.
    Эффективность лечения оценивалась следующим образом:
  • Заметно эффективный: неподвижная ЭКГ восстанавливается в нормальное положение, толерантность движения повышается в 3 раза;
  • Улучшение: ишемическая часть ST неподвижной ЭКГ или ЭКГ испытания движения вторичной предельной величины после лечения поднимается более, чем на 1,5 мм, но еще ненормальна;
  • Нет изменений: неподвижная ЭКГ или ЭКГ движения вторичной предельной величины в основном одинакова с той, что было до лечения;
  • Обострение: часть ST ЭКГ неподвижной или ЭКГ испытания движения вторичной предельной величины снизилась более, чем на 0,5 мм, чем до лечения, инверсированная волна Т главного отведения прибавляется более, чем на 50%; или ортостатическое положение волны Т переходит в ровное положение, или ровное положение волны Т переходит в инверсию, или испытание движения вторичной предельной величины снижается на одну ступень, чем прежняя толерантность движения.




  • Число случаев %
    Заметн.
    эффект
    Эффектив. Неэффек. Итого Заметн.
    эффект
    Эффект.
    Группа А (Оксикардин) 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: Еще более сухое и, возможно, более корректное (или истинное, чем черт не шутит?) доказательство Догадки прислал Тим Туманный. Читайте!.



    Еще раз повторю формулировку Догадки:

    "Каждое четное число, большее двух,
    может быть представлено
    в виде суммы двух простых чисел."


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

    Причем любой здравомыслящий человек, конечно же, понимает, что никакого хаоса нет, и вот это-то и раздражает больше всего. Если нет хаоса - значит, есть система. Видимо, сложная, но все-таки система. Так в чем же она?

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

    Пролетая над миллионом баксов
    Допустим, перед нами стоит задача определить, является ли простым некое число (на рисунке - любимое число 13, для примера). Естественной процедурой в таких случаях является поочередная попытка деления тестируемого числа на все числа, меньшие его, начиная с 2, на предмет определения делимости нацело. Смешно об этом говорить, но если число делится без остатка только на самое себя, то оно простое.

    Нарисуем все потенциальные делители так, как показано, в виде кирпичной кладки.

    Идея, по-моему, достаточно очевидна. Например, делитель "2" укладывается в кладке до отметки "13" шесть раз целиком, и еще пол-кирпича торчат наружу. А вот делитель "3" - четыре раза целиком и еще кусочек. И так далее.

    Мы видим, что на отметку "13" попадает стык кирпичей в одном-единственном ряду - в ряду "13". Не нужно быть бакалавром математики, чтобы сообразить, что смысл этого рисунка, если исключить из рассмотрения избыточный нижний ряд, можно сформулировать следующим образом: "Если на отметку числа не попадает ни один стык кирпичей, то оно простое".



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

    Пролетая над миллионом баксов


    Мама дорогая! Так это ж все сплошь простые числа! Вот оно, первое гениальное прозрение вольного каменщика: "Чтобы выяснить, является ли число простым, достаточно установить отсутствие его делимости нацело на все простые числа, меньшие его."

    Уважаемые товарищи программисты! Не загружайте процессоры бесполезной работой! При определении простоты чисел используйте в качестве делителей только простые числа! (За исключением случаев, когда вы пишете на JavaScript. Тогда вы можете на этот призыв наплевать - все равно скрипт работает на чужом компьютере. Именно так я и сделал в этом примере.)

    Впрочем, все вышесказанное никаких откровений в себе не содержит и давно всем очевидно. Не волнуйтесь: это всего лишь подготовка строительной площадки. А вот теперь, еще раз хорошенько всмотревшись в кирпичи, мы готовы сделать действительно принципиально важное для доказательства Догадки Гольдбаха умозаключение. Вот оно:

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


    Каждый ряд в кирпичной кладке - это периодический процесс с длиной периода, равной соответствующему ему простому числу. В точке 0 числовой шкалы узлы всех периодов совпадают. Очередное простое число образуется в точке, в которой периоды всех предыдущих простых чисел не имеют узлов. Следующее простое число образуется в новой точке, зависящей не только от всех предыдущих простых чисел, но и от вновь найденного.



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

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

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

    Пролетая над миллионом баксов
    Этот рисунок уже прямо иллюстрирует Догадку и способ ее подтверждения. Для примера мы взяли число 16, которое, как мы знаем, является четным, и, если верить Гольдбаху, должно иметь два простых слагаемых. Не станем томить, таковые действительно имеются. Например, 11 и 5.

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

    Пролетая над миллионом баксов
    Целью всех этих, без сомнения, допустимых манипуляций, является переформулировка Догадки Гольдбаха в таком виде: для каждого четного числа можно составить хотя бы один вариант описанной кирпичной кладки, в котором имеется точка рандеву.

    Почему сказано "вариант кладки" - потому что возможны и другие варианты, как показано на рисунке справа.

    Давайте еще раз рассмотрим рисунок img03, ибо он позволяет сформулировать условие существования точки рандеву. Видно, что существует 15 точек, каждая из которых потенциально может стать точкой рандеву. Но всем, кроме одной, мешают стыки рядов кирпичей, иногда одного, а иногда и сразу нескольких. Разберемся с каждым из рядов по отдельности.



    Правда, на рисунке img04 есть еще точка, отстоящая слева на 1, а справа - на 15, которой не мешает ни один стык кирпичей. Такая точка имеется у всех пар простых слагаемых, одно из которых - 3. Существование этой точки еще ждет своего объяснения, и, я думаю, кирпичная теория простых чисел способна дать его.

    Видно, что желтый и голубой ряды кирпичей "2" синфазны. Их стыки совпадают. Это не удивительно, ведь число 16 кратно 2. Эти ряды будут совпадать и для любых других четных чисел. И они отсеивают половину всех потенциальных точек рандеву. Если бы фазы голубого и желтого рядов "2" не совпадали, тогда совместными усилиями они бы отсеяли все точки. Однако фазы рядов "2" не совпадают только у нечетных чисел.

    А вы-то, небось, думали, что нечетное число не может быть суммой двух простых чисел, потому что сумма двух нечетных всегда четна? На самом-то деле причина в кирпичах!

    А вот ситуация с желтым и голубым рядами "3" гораздо интересней. Поскольку число 16 не кратно 3, то фазы этих рядов не совпадают. В результате ряды "3" еще отнимают каждые две из трех потенциальных точек рандеву, оставшихся после разгрома, учиненного рядами "2". Очевидно, что после их совместных усилий остается вот какая доля потенциальных точек рандеву:

    Пролетая над миллионом баксов
    (1)

    Следующий ряд - "5". К сожалению, на рисунке img03 ситуация не очень удачная - нет голубого ряда "5", потому что число 16 слишком маленькое. То же касается и ряда "7". Впрочем, мы уже достаточно подготовлены, чтобы забросить возню с кирпичами и обратиться-таки к абстрактным рассуждениям.

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



    Пролетая над миллионом баксов
    (2)

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

    Пролетая над миллионом баксов
    (3)

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

    Пролетая над миллионом баксов
    (4)

    Здесь:
  • qi - очередной (i-й) член прогрессии
  • Pi - соответствующее этому члену простое число из ряда простых чисел, начиная с 2


  • Также очевидно, что прогрессия (4) бесконечно стремится к 0.

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

    Пролетая над миллионом баксов
    (5)

    Поясним, откуда здесь что.

    (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 прислал письмо:

    Добрый день!

    Очень простое и наглядное дидактическое пособие для объяснения

    того, когда начинается новый век:

    "Второй ящик водки начинается с двадцать первой бутылки". :))
    Это, на наш взгляд, очень интересная и перспективная версия, хоть она и отметает все сказанное выше. К сожалению, подтвердить ее или опровергнуть столь же трудно, как, скажем, Догадку Гольдбаха. Во всяком случае, при проведении многочисленных экспериментов assembler.ru даже вместе с друзьями, даже под шашлык, никогда не добирался дальше восьмого века н.э. Было бы здорово, если бы кто-нибудь из неравнодушных к науке посетителей взялся бы проверить эту версию. Спонсировать, к сожалению, не сможем, но отчет опубликуем с удовольствием. (Репортажи со свадеб и юбилеев просьба не предлагать.)

    Ну и в заключение - маленькая сенсация. Основываясь на той же методике, порассуждаем не о годах, а о веках.

    Первое тысячелетие составляли века: 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 push R1 ENDIF IFNB push R2 ENDIF IFNB push R3 ENDIF IFNB push R4 ENDIF IFNB push R5 ENDIF IFNB push R6 ENDIF IFNB push R7 ENDIF IFNB push R8 ENDIF ENDM ;==================================================================== ;Макрос popr

    ;Работает так же, как и pushr, за одним исключением: очередность проверки ;обратная. Это позволяет программисту не менять очередности упоминания ;параметров при использовании макроса в программе. ;==================================================================== popr MACRO R1,R2,R3,R4,R5,R6,R7,R8 IFNB pop R8 ENDIF IFNB pop R7 ENDIF IFNB pop R6 ENDIF IFNB pop R5 ENDIF IFNB pop R4 ENDIF IFNB pop R3 ENDIF IFNB pop R2 ENDIF IFNB pop R1 ENDIF ENDM ;#################################################################### ;Вариант SECOND

    ;//////////////////////////////////////////////////////////////////// ;Вспомогательные макросы

    ;==================================================================== 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 ;синтаксис указания параметров: fcnt=0 ;создание списка операндов IRP s,&args fcnt=fcnt+1 makevar t1,%fcnt,s ENDM fcnt=0 ;создание набора команд push операнд

    IRP s,&args fcnt=fcnt+1 makevarn t1,%fcnt ENDM ENDM ;==================================================================== ;Макрос popr

    ;==================================================================== popr MACRO args ;синтаксис указания параметров: fcnt=0 ;создание списка операндов IRP s,&args fcnt=fcnt+1 makevar t2,%fcnt,s ENDM IRP s,&args ;создание набора команд pop операнд

    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 push R1 pushr R2,R3,R4,R5,R6,R7,R8 ENDIF endm ;==================================================================== ;Макрос popr

    ;Видно, что этот макрос также рекурсивный, но количество итераций ;макрорасширения определяется в нем не числом фактических аргументов, ;как в pushr, а числом формальных параметров (здесь - 8), пока все ;они, сдвигаясь вправо, не заменятся заглушкой dummy. При этом ;реальный код команд pop будет сформирован только для параметров, ;которым соответствуют действительные фактические аргументы. ;==================================================================== popr MACRO R1,R2,R3,R4,R5,R6,R7,R8 IFNB IFIDN , EXITM ENDIF pop R8 ENDIF popr dummy,R1,R2,R3,R4,R5,R6,R7 ENDM

    Рекомендуем обратить внимание на суть заглушки 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
    Рис.1. Общая структура файла

    Заголовок файла - это набор структурированных полей фиксированной длины. На рисунке 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 Обычно не используется
    Рис.2. Заголовок PCX файла

    В первой графе указывается порядковый номер поля, который приведен для более легкой ориентации в таблице и более легкого изложения материала. Во второй - смещение до начала поля в десятичном и шестнадцатиричном исчислении. Третья содержит название, которое сохранено таким же, как и в пакете 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 регистров палитры:

    О формате pcx


    Рис.3. Формат поля номер 11

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



    О формате pcx


    Рис.4. Расположение данных 256 - цветной палитры

    Для того, чтобы попасть на это поле, необходимо:

  • установить указатель в конец файла;
  • передвинуть его на 769 байтов назад;
  • проверить, равен ли адресуемый байт значению 0Сh или 0Ah (зависит от программы, формировавшей pcx-файл).


  • Делать это, конечно, необходимо в том случае, если вы уверены, что 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:

    О формате pcx


    Рис.5. Кодирование длинных серий

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

    Очевиден и способ декодирования данных:

  • Проверить равны ли оба старших бита считанного байта 01. Если равны, то следующий байт необходимо повторить столько раз, сколько получится из байта-счетчика после обнуления двух его старших битов.
  • В противном случае это - байт-эталон, и его нужно повторить один раз.


  • Отметим, что в случае, когда байт-эталон имеет установленные стаpшие биты в единицу (напpимеp, чеpточка длиной в 8 пикселей и следом за ней 8 чеpных пикселей пpедставляются в виде двух байтов 0FFh,00h), то из-за этого он похож на байт-счетчик, и его пpедваpяют байтом-повтоpителем, pавным единице. Этим снимается неоднозначность опpеделения типа данных.



    Теперь, для завершения обсуждения вопроса о кодировании, покажем, каким образом располагаются в файле данные, хранящие изображение.

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

    О формате pcx


    Рис.6. Порядок следования данных

    программа обработки файла pcx

    В общем случае программы должны обладать возможностью и создавать, и интерпретировать файл этого формата.

    Под интерпретацией здесь понимается возможность правильно понимать данные, хранящиеся в заголовке pcx-файлов, и в соответствии с ними (данными заголовка) строить на экране изображение.

    Как правило - это мощные графические редакторы или утилиты, им сопутствующие. Но есть задачи, не требующие создания файлов формата PCX. Необходимо только второе - интерпретировать. Как правило, это универсальные программы-вьюеры. Есть задачи и более простые - показать на экране рекламное изображение. Для этого надо совсем мало, так как в связи с неизменностью условий работы программы практически отпадает необходимость подробно анализировать заголовок файла PCX. Такая задача и предлагается вашему вниманию.

    Наилучший способ объяснить - это привести пример. Поэтому приступим к изложению задачи, которую необходимо решить и к описанию программы, ее реализующей.

    Построение любой программы начинается с четкого уяснения условий, поставленных задачей. Каковы же эти условия?

  • минимальный размер кода
  • минимально возможный и фиксированный размер занимаемого пространства при выполнении программы
  • быстрота вывода
  • способность "понимать", возможна ли обработка данного pcx-файла
  • разбор палитры и инициализация регистров палитры видеоадаптера
  • независимость от того, каким из графических редакторов создавался pcx-файл (полной независимости конечно, не бывает)
  • режим работы монитора с разрешением 640х350 (режим 10h для адаптеров EGA/VGA)
  • pcx-файл должен находиться отдельно от программы вывода


  • Первые два условия объясняются желанием не увеличивать "безгранично" размеры программы*), к которой пристыковывается этот модуль, и, во-вторых, быть независимым от размеров pcx-файла и от типа (.com или .exe) основной программы. Предваряя результат, скажем, что размер исполняемого модуля менее одного килобайта. А процедуры, относящиеся непосредственно к декодированию и выводу изображения, занимают около 600 байт.



    *) Это ухудшило бы потребительские характеристики программы. Например, если вы хотите использовать какой-либо популярный драйвер или утилиту (с разрешения автора) для рекламы то было бы совершенно неприемлемо увеличивать размер программы на 10 или больше килобайт.

    Это объясняется тем,что программа написана на языке ассемблера, и тем, что поставленная задача проста, и поэтому количество процедур мало. Это же обеспечивает выполнение и третьего условия.

    Под способностью "понимать" имелась ввиду реализация программного блока, анализирующего необходимые поля заголовка pcx-файла, и, в зависимости от их содержимого, формирующего команды, позволяющие программе ветвиться.

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

    Говоря о шестом условии, подчеркнем, что существует большое разнообразие нюансов при записи данных в pcx-файл.

    Например, автор столкнулся с тем, что достаточно часто графические редакторы или утилиты формируют pcx-файл, считая некоторые характеристики изображения неизменными (например, размер текстовой строки считается величиной постоянной и равной 80 символам в графическом редакторе PMTV, поставляемом совместно с ручным сканером "A4SCAN" фирмы A4TECH CO.ltd.(U.S.A.)).

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

    Поясним это утверждение следующим примером.

    Предположим, мы нарисовали линию белого цвета длиной в 35 пикселей. Размер же всего изображения примем равным 67x1 пикселей, для упрощения рисунков и рассуждений:

    О формате pcx


    Рис.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 и других.

    На этом завершим комментарии к условиям задачи и перейдем к составлению и обсуждению блок-схемы:

  • первое, что необходимо сделать - это проверить, передается ли имя файла, и если да, то проверить, существует ли такой файл.
  • при положительном результате проверки прочитать в буфер программы заголовок pcx-файла.
  • затем посмотреть поле 03 (encode) и убедиться, что это известный нам способ кодирования. Далее прочитать поле 12 (vmode) и проверить, сможем ли мы обслужить обозначенный видео-режим.
  • в завершение проверить поля 01 и 02 и убедиться, что это известные нам фирма и номер версии. После этого можно приступить к подготовке данных, необходимых для дальнейшей работы.

    В исходном тексте нашей программы отсутствует такой подробный разбор, так как для данной задачи вышеописанные условия известны заранее. Поэтому после подготовки данных можно установить необходимый видеорежим дисплея, вывести изображение и остановиться в ожидании команды пользователя. По команде восстановить исходный режим и завершить программу (см. блок-схемы на рис.9).



    Основная программа OPCX#4. Процедура EGAP

    О формате pcx


    Рис.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.

    Внимание не заострялось на этом моменте, так как это не являлось основной темой.

    О формате 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 лет был главным редактором бюллетеня этого проекта.)
    Комментированный сборник ссылок на международные ассемблерные ресурсы, полезные для разных категорий программистов. Содержание:
  • Почему ассемблер - мнение Дональда Кнута (Donald Knuth)
  • Проекты MMIX, TAOCP Дональда Кнута
  • Руководства и учебники по ассемблеру
  • Опыт ведущих мировых специалистов
  • FAQ
  • Программирование на ассемблере для Linux, Windows
  • Дизассемблирование и дизассемблеры
  • Коллекция исходных текстов

  • Страница обновляется эпизодически. Последние материалы относятся к 1999 - 2000 году.
    [+] Programmers' Heaven
    Крупный сервер по программированию. Владелец - Synchron Data. Обновляется регулярно. Содержит оригинальные материалы по ассемблеру:
  • Компиляторы и дизассемблеры
  • Отладчики
  • NASM: программа, документация, примеры
  • Математические процедуры (генераторы случайных числе, вычисление пи, плавающая арифметика и т.д.)
  • Десятки коллекций исходных текстов
  • Программы защищенного режима
  • Утилиты

  • В разделе Demos большое количество разнообразных демонстрационных программ. Разделы, посвященные ассемблеру, в основном содержат материалы по системному программированию, непосредственной работе с компьютером на уровне архитектуры, решению математических задач. Рассматривается архитектура не только x86, но и 680, и др. Материалов по прикладному программированию (в т.ч.для Windows) очень мало.
    [+] NASM - The Netwide Assembler Project
    Сайт о проекте NASM - свободно распространяемом ассемблере для x86. Содержание:
  • Что такое NASM
  • Кто создал NASM
  • Где взять NASM, ссылки на ресурсы, посвященные NASM
  • Версии NASM
  • Баг-лист
  • Полное руководство по NASM
  • Чат-форумы по NASM
  • Инструменты и библиотеки
  • Описание RDOFF - формата объектных файлов NASM

  • [+] 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-х годов. Содержание:

  • Основы ассемблера, программирование сопроцессора (FPU, x87), bootstrap - процедура (самозагрузка и запуск ОС с дискового устройства)
  • Учебник для начинающих: введение в ассемблер, сегментная модель, ввод-вывод в DOS, файловые операции в DOS
  • Программирование VGA для игр: основы, скроллинг, анимация, 3d, полигоны, Chain-4, crossfade (чресстрочная развертка, чередование полей), 3-d зведное поле, прозрачность цветных 3d-объектов (glenzing), программирование плазмы, масштабирование bitmap (scaling), пиксельный морфинг, формат pcx-файла, программирование огня, face sorting (оптимизация перерисовки 3d-сцен), наложение текстур
  • Формат bmp-файла, программирование Adlib / Sound Blaster FM - чипа (midi), формат midi-файла, программирование Sound Blaster 16 DSP
  • Программирование DMA, описание протокола TCP-IP


  • [+] PC-GPE on the Web

    Автор - Brand Huntsman. Страница содержит только один учебник для начинающих "VLA Tutorial" - тот же самый, что на Icey-Dee's Programming Site.

    [+] Assembly languages

    Сайт об ассемблере, автор - John English. Содержание:

  • Инструменты разработчика: NASM (freeware,1997), A86 (shareware,1995), TMA (macro assembler, 1998), 68K Edit (кросс-среда разработки на ассемблере процессора Motorola 68000 для Windows 95/98/NT, freeware, 1999), T68K (то же, включая C. Имеются исходные тексты), отладчики, дизассемблеры и т.д.
  • Литература: Randall Hyde, The Art of Assembly Language Programming (классический учебник, 144 файла, 3,5 Мбайт), Gavin Estey, Gavin's Guide to 80x86 Assembly (известный учебник,8 файлов, 75 Кбайт), FAQ по MASM, TASM, A86, 8086, разработке игр
  • Великолепный сборник исходных кодов: системные утилиты, средства разработки, диагностика и бенчмарки, редакторы, архиваторы, игры, графика, интернет, языки программирования, библиотеки и т.д.




  • [+] ASSEMBLE IT! - ASM orientad page

    Страница об ассемблере, автор - Ferdi Smit. Содержание:

  • Авторская коллекция исходных кодов
  • Документация: учебник дла начинающих (автор - "Mad")


  • Страница создана в 1996 году, давно не обновлялась, многие ссылки мертвы

    [+] MoonWare Home Page

    Давно заброшенная (1996) страница известного разработчика Raymond'а Moon'а. Содержит довольно приличный сборник ссылок на старые ассемблерные ресурсы, подборку утилит (в основном архиваторы) и FAQ (те же, что в Assembly languages).

    [+] Open Directory:Top:Computers:Programming:Languages:Assembly

    Ассемблерный раздел грандиозного проекта Netscape под названием Open Directory, призванного ответить на вызов, брошенный сетевым каталогам взрывообразным ростом интернета. Идея заключается в том, что каталог Open Directory должны пополнять добровольные редакторы, которыми могут быть любые люди. Пока не очень получается: в разделе чуть больше 10 ссылок. Имеются подразделы:

  • 370
  • Amiga
  • 68k
  • PalmPilot
  • x86 (около 30 ссылок, многие мертвы)
  • z80


  • [+] 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, сочетающее в себе кросс-платформенный компилятор и компоновщик (с возможностями компоновки ресурсов). На сайте имеются:

  • Руководство пользователя Pila
  • ссылки на ASDK (Alternative Software Development Kit) для Pilot
  • новости из области разработки ПО на ассемблере для Pilot


  • [+] 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), в том числе:

  • Спецификация boot-процессов с различных устройств, в том числе CD-ROM, форматы загрузочных секторов
  • Разбиение жестких дисков на разделы (partitions)
  • Файловые системы, в том числе CD-ROM
  • Управление памятью EMS,XMS
  • Средства работы с процессором в защищенном режиме (VCPI, DPMI, VDS, VOODOO...)
  • Форматы исполняемых файлов (com, exe), библиотек (lib), объектных модулей (obj)
  • Интерфейсы драйверов устройств (Device Driver Interfaces)
  • Архитектура процессоров (IA64, MMX, 3D-Now!, KNI, copro...)
  • Шинная архитектура (ATA, Floppy, PCI, AGP, USB, SCSI...)
  • Диски и драйвы
  • Оборудование пользовательского интерфейса (клавиатура, мышь, джойстик, gamepad...)
  • Звуковые карты и звуковоспроизведение (SB, SB16, PC-Speaker, MIDI, OPL...)
  • Коммуникационные порты (COM, LPT)
  • Прочее оборудование (PIC, PIT, DMA, RTC, ATX...)


  • Другие страницы ссылок на assembler.ru:

  • рунет : win32asm

    ассемблер

    братские сайты


  • internet : win32asm


  • избранное : форумы

    документация

    разное



  • Internet : win32asm


    if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
    [+] Iczelion's Win32 Assembly Homepage
    Самый известный в Интернет ресурс о программировании на ассемблере для Windows. Регулярно обновляется и расширяется. Разделы:
  • учебные материалы:_masta_'s Win32Asm - введение в 32-битный ассемблер для Windows
    Win32 Assembly - сборник статей (более 30) о программировании для Windows
    VxD Tutorials - основы разработки виртуальных драйверов устройств для Windows
    формат PE-файла и работа с ним
    работа с ODBC

  • статьи разных авторов:как писать VxD на NASM
    как писать NT сервисы на ассемблере
    поддержка исключений в программах на ассемблере под Windows
    как использовать DirectDraw
    и др.

  • сборник исходных текстов разных авторов на ассемблере под Windows(около 40)
  • сборник программного обеспечения, в том числе:MASM32 - пакет на основе MASM 6.14, включающий в себя полный комплект средств, необходимых для разработки приложений на ассемблере
    файл заголовков API windows.inc, постоянно обновляемый
    последние обновления компилятора MASM
    справочники по функциям API
    специализированный редактор программиста UltraEdit
    и пр.

  • обширный сборник ссылок на англоязычные ресурсы о программировании на ассемблере для Windows

  • [+] Assembly Programming Journal
    Сетевой журнал о программировании на ассемблере. Выходит с октября 1998 каждые три месяца и содержит статьи и код для начинающих, опытных и продвинутых программистов на ассемблере. Редактор - mammon_. Ведущие разделов - Iczelion, mammon_, Xbios2. Кроме статей, сайт содержит большой раздел ссылок на ассемблерные ресурсы, форум, библиотеку примеров, программное обеспечение, в том числе бесплатное для программирования под win32:
  • NASM - компилятор
  • GoRC - компилятор ресурсов для win32
  • ALIB - генератор файлов библиотек для ALINK
  • ALINK - 32-битный компоновщик
  • BIEW - редактор двоичных файлов
  • Borg - дизассемблер win32
  • GoVEST - отладчик для win32
  • QEditor - редактор программиста
  • Win32.lib - библиотеки API win32 для ALINK


  • [+] Whiz Kid Technomagic by G. Adam Stanislav

    Автор - G. Adam Stanislav, независимый программист, Rhinelander, Wisconsin. Сайт открыт 10.01.1997. Содержит примеры исходных текстов на ассемблере для Windows 95:

  • Пример DLL
  • DLL для определения направления прорисовки полигона, определения выпуклости, вычисления площади полигона с помощью сопроцессора
  • Работа с таймером, мышью, изображением bitmap в памяти, копированием bitmap в буфер обмена
  • Открытие окна web-броузера по умолчанию и заданной web-страницы
  • DLL для генерации псевдо-случайных чисел
  • Примеры plug-ins для Photoshop, написанных целиком на ассемблере


  • Мнение автора:

  • Негативное - о технологии OLE/COM
  • Позитивное - о программировании на ассемблере для Windows


  • Избранное : документы


    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:
  • рунет : win32asm
    ассемблер
    братские сайты

  • internet : win32asm
    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:

  • Пример DLL
  • DLL для определения направления прорисовки полигона, определения выпуклости, вычисления площади полигона с помощью сопроцессора
  • Работа с таймером, мышью, изображением bitmap в памяти, копированием bitmap в буфер обмена
  • Открытие окна web-броузера по умолчанию и заданной web-страницы
  • DLL для генерации псевдо-случайных чисел
  • Примеры plug-ins для Photoshop, написанных целиком на ассемблере


  • Мнение автора:

  • Негативное - о технологии OLE/COM
  • Позитивное - о программировании на ассемблере для Windows



  • [+] 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:
  • рунет : win32asm
    ассемблер
    братские сайты

  • internet : win32asm
    assembler

  • избранное : форумы
    документация



  • Рунет : ассемблер


    if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
    [+] Домашняя страничка Алексея Фрунзе
    Алексей программирует на Borland Pascal, TASM, MASM, NASM, Watcom C и DJGPP. Сайт обновляется регулярно. Содержит авторские разработки:
  • Frounze Commander
  • 3-х мерные движки (доступны исходные тексты)
  • демонстрационные программы, в том числе по 2-х-мерной и 3-х-мерной графике (текстурирование, z-буфер), звуку, работе с системными ресурсами (память, сеть, порты и пр.)
  • обучающие программы по тематике защищенного режима и разработке ОС: 32-битный защищенный режим 386+ процессора, обработка исключений, обработка прерываний, переключение задач, страничная адресация, монитор ражима v86. Исходные тексты для TASM 3.2+
  • COFF-утилиты, преобразующие объектные файлы в исполняемые. Исходные тексты для NASM и C (компилятор DJPP)
  • программы загрузочного сектора, обеспечивающие запуск программ с диска. Исходные тексты для NASM
  • загрузчик ОС, разработанный в рамках проекта OS Development for Dummies

  • статьи по программированию микроконтроллеров

  • Имеется большая подборка разнообразной документации (в виде zip-архивов):
  • по 2-х-мерной и 3-х-мерной графике (более 10 документов, 10...600 Кбайт, 3 на русском)
  • описание формата файла 3d Studio
  • алгоритмы архивации / компрессии (8 документов, 4 на русском)
  • докуметация по программированию на TASM и MASM (6 документов, 75...500 Кбайт, 4 на русском)
  • Книга по программированию Дэвида Брэдли (на русском)
  • документы и книги по программированию на C/C++ (15, 10...500 Кбайт, в основном на русском)
  • справочники по форматам файлов (coff, elf, exe, gif, omf)
  • программирование жестких дисков (7 документов, 2 на русском)
  • программирование процессоров Intel (8 документов, 4 на русском)
  • оптимизация программ, в том числе для Pentium
  • программирование на Паскале (более 10 документов, в основном на русском)
  • The great PC Games Programmers Encyclopedy 1.0 (746 Кбайт, английский). Огромное количество советов и примеров, специфических для прогрмааирования игр
  • документы по программированию CD-ROM, мыши, портов, LAN, Sound Blaster, DPMI, драйверов для DOS, видеоадаптеров, файловых систем


  • Assembly Language database, Interrupts database, TASM2.0 database от Петера Нортона и его команды
  • BIOS Technical Reference
  • несколько описаний Dos Protected Mode Interface (DPMI)
  • знаменитый PC Interrupt List Ральфа Брауна и много других документов по прерываниям и портам
  • документация по аппаратной части компьютера (в т.ч. VGA, мышь) и работе с ней
  • документация по работе с EMS и XMS
  • techhelp и прочее


  • [+] RPC Software

    Сайт о программировании. Обновляется еженедельно, имеется рассылка новостей и форум. В разделе об ассемблере несколько документов на русском языке:

  • пособие по ассемблеру для DOS
  • архитектура ввода-вывода персональных ЭВМ
  • макроассемблер
  • формат PE-файла
  • оптимизация программ для Pentium
  • руководство разработчика для MS-DOS
  • справочник по Турбо-ассемблеру 2.0 и 3.0
  • документация по ТASM 3.0


  • В разделе "Программы и исходники" - демо-версия игры "Марио", пример вируса

    [+] SOFT MUSEAM

    Персональная страничка неизвестного автора. Содержит несколько небольших программ на ассемблере для DOS. Одна из программ представляет собой компилятор-компоновщик com-файлов для разработанного автором элементарного ассемблера с простым и интересным синтаксисом.

    [+] Компания Free Programmers

    Компания Free Programmers - это трое единомышленников, занимающихся разработкой собственной операционной системы FPOS. ОС находится в начальной стадии, также как и сайт. Разработчики имеют опыт программирования процессора в защищенном режиме.

    [+] Страница Панькова Дмитрия

    Страница о программировании на ассемблере для DOS. Содержит материалы:

  • описание команд ассемблера (html)
  • информация о MS-DOS (html)
  • описание BIOS (rar 60 Кбайт)
  • описание MS-DOS (zip 300 Кбайт)
  • описание MS-DOS (zip 100 Кбайт)
  • недокументированные возможности MS-DOS (zip 13 Кбайт)
  • описание адаптера VGA (zip 140 Кбайт)
  • описание адаптеров EGA и VGA (zip 140 Кбайт)
  • библия MS-DOS (zip 300 Кбайт)


  • [+] AsmJournal на русском

    Сайт существует с ноября 1999 года, автор - Heir To Empire. Основным содержанием сайта должны являться русские переводы выпусков журнала Assembly Programming Journal - одного из самых ценных ассемблерных ресурсов в Сети. В настоящее время в списке числятся восемь выпусков журнала - с октября-1998 по март-2000. Кроме того, сайт имеет раздел по программированию для Unix/Linux и раздел ссылок. К сожалению, проверить, насколько содержание сайта соответствует заявленному, оказалось невозможным. Сайт обильно использует JavaScript и все внутренние ссылки неработоспосбны ни в IE, ни в NN. По крайней мере, у нас.



    [+] Проект "Компьютерный Клуб"

    Автор - Александр Гацко. Сборник документов и ссылок по программированию. Раздел об ассемблере содержит более 20 ссылок на документы, размещенные на различных англоязычных сайтах. Тематика документов касается в основном архитектуры и применения процессоров Intel и AMD. Из русскоязычных документов имеются в ссылках на www.intel.ru:

  • справочное руководство по MASM
  • Мультимедиа курс "Архитектура Pentium Pro"
  • Мультимедиа курс "Оптимизация программ для Pentium"
  • Мультимедиа курс "Новые команды SIMD расширения Pentium III"
  • Мультимедиа курс "IA MMX расширение"


  • Также имеются документы по криптографическим алгоритмам, распознаванию образов, математике, базам данных, языкам программирования, операционным системам, программированию под Windows, под MS-DOS и пр.

    Арихтектура и дизайн сайта имитируют разветвленное файловое дерево. Поиск необходимого раздела иногда требует довольно большого количества шагов.

    hardware

    [+] CHIPINFO

    Совместный проект организаций и предприятий, специализирующихся в области разработки, производства и продажи электронных компонентов: НПФ "Радио-Сервис", "Макро Тим", МТУСИ, "Оптоника", "ПромАвтоКонтракт", "Точка Опоры", "АВ-Центр", "Квазар-Микро", New Technologies, "Паллада-Микро". CHIPINFO - пожалуй, единственный на сегодняшний момент сервер, имеющий собственную базу данных по электронным компонентам с возможностью полнотекстового поиска информации об электронных компонентах как по наименованию, так и по функциональным возможностям. При работе с сервером доступны:

  • техническая документация для 80486 электронного компонента
  • коллекция принципиальных схем
  • обзор каталогов ведущих мировых производителей
  • WEB-сервера и адреса более 900 производителей электронных компонентов
  • адреса и телефоны российских дистрибьютеров электронных компонентов
  • программное обеспечение для проектирования электронных устройств
  • система "Виртуальный склад" для продавцов и покупателей электронных компонентов
  • форумы: Куплю, Продам, Ищу, Документация, Схемы, Платы, Интернет, Программы, Автоэлектроника, Бытовая электроника, Компьютеры, Предложения, Работа
  • web-кольцо ресурсов о российской электронике (более 50 участников)




  • каталоги, сборники

    [+] 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

    Сборник различных файлов, в том числе текстовых документов. Специального раздела по ассемблеру нет, но в разных разделах имеется большое количество интересных документов:

  • Справочная система по языкам программирования (ассемблер под DOS)
  • Справочная система по языку программирования Assembler
  • Пособие по слому и написанию простейшего VxD-Эмулятора
  • Опыт дизассемблировения большой .СОМ программы
  • Как написать компьютерный вирус
  • Программирование с использованием MMX
  • Программирование в графических режимах VGA
  • Iczelion's Win32asm tutorial
  • Примеры программирования на ассемблере под Win32
  • "АSSEMBLER & WIN32"
  • "ВИРУСЫ", "ЧЕРВИ", "ДРАКОНЫ" И РЕЗИДЕНТЫ НА СЛУЖБЕ ПРОГРЕССА при изучении языка ассемблера и системы MS-DOS
  • Linux Kernel Module Programming Guide
  • Руководство программиста для Linux
  • Assembly Language Programming for Windows 95
  • Система программирования на макро ассемблере MS-DOS
  • Справочник по системе программирования на Turbo Assembler 2.0
  • Turbo Debugger 3.0
  • и пр.


  • объявления

    Привет всем!

    Кто занимается 2D и 3D графикой на ассемблере, пишите все, кого интересует графика на ассемблере на мой ящик Cheremgtk@mail.ru, будем вместе обсуждать и решать возникающие проблемы, а также обмениваться своим опытом, сообщать об своих успехах, короче быть в центре всех событий по графике на ассемблере.

    С уважением,

    Шойко Александр

    Другие страницы ссылок на assembler.ru:

  • рунет : win32asm

    братские сайты


  • internet : win32asm

    assembler


  • избранное : форумы

    документация

    разное



  • Рунет : братские сайты


    Рунет : братские сайты

    HI-TECH - Сайт изобретателей фонариков на солнечных батарейках


    Сайт о низкоуровневом программировании. Его базой служит почтовая рассылка на сервере Subscribe.Ru, которая называется "Низкоуровневое программирование для дZeнствующих" и целиком посвящена программированию на ассемблере, начиная с самых фундаментальных его основ.
    Предыдущие выпуски рассылки:
  • #1 Об авторах -16.10.00
  • #1+ Компьютер состоит из... -17.10.00
  • #2 Введение в курс. Система счисления -25.10.00
  • #3 Первое погружение. Виды памяти. Загрузка машины. Область памяти BIOS -02.11.00
  • #4 Первое погружение в регистры... -19.11.00
  • #4+ История сотворения мира (типа юмор)... -20.11.00
  • #5 Программа в памяти... -26.11.00
  • #6 Первое погружение в прерывания -03.12.00
  • #7 Продолжаем программировать и учимся "отлаживать" -10.12.00
  • #8 Взлом игр. Разборки со стеком, циклами, оптимизацией -19.12.00
  • #9 Разборки с процедурами и CX=10000h -25.12.00
  • #10 DZebug: полное руководство юзвера, часть 1 -31.12.00
  • #11 DZebug: полное руководство юзвера, часть 2 -07.01.01
  • #12 Разборки с переходами. Пишем "рисовалку"... -14.01.01
  • #13 Обращаемся к сегменту данных. -21.01.01
  • #14 Второе погружение в регистры. -29.01.01
  • #15 Первое погружение в компиляцию. -04.02.01
  • #16 ИZDранное из RTFM_Helpers. -11.02.01
  • #17 Вирий, Hello World и "Кодирование информации в цифровых компьютерах". -18.02.01
  • #18 Статья "Алгебра логики и цифровые компьютеры". -25.02.01
  • #19 Разминаем пальцы на небольших (временно глупых) процедурках. -13.03.01
  • #20 Печать шестнадцатеричных циферек :). -06.04.01
  • #21 Печать десятичных циферек, "Hello world" или Изврат-II, первое знакомство с флагами. -25.05.01


  • Рунет : братские сайты
    Подпишитесь на рассылку
    с помощью этой формы!


    Кроме этого, на сайте имеется обширный раздел DOWNLOAD, содержащий:
  • Компиляторы, Интегрированные среды, Дизассемблеры, Отладчики
  • Кроссассемблеры
  • Шестнадцатиричные редакторы, Редакторы ресурсов
  • Упаковщики, Распаковщики
  • Модификаторы, Конвертеры, Анализаторы
  • ANSI/ASCII-графика, DEMO-сцена, Генераторы фрактальных картин
  • Крипто-алгоритмы
  • Исходники под Win32, DOS, DEMOк
  • Справочные системы, Документация
  • Ломалки игрушек, Фиксы, Артефакты


  • Рекомендуем также посетить еще один сайт Алексея http://welcome.to/pmode/ - о программировании защищенного режима. На сайт выложены почти два десятка примеров программ, от простого к сложному, что важно - написанных на бесплатных реализациях ассемблера, с комментариями и пояснительным текстом.

    Рунет : братские сайты


    Отдел Исследования Программ

    Это один из самых популярных в Рунете ресурсов о программировании.

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

    В разделе Инструменты вы найдете большое количество программ, которые позволят успешно решить множество разнообразных задач: от сжатия готового исполняемого модуля или его шифрации, до восстановления исходного текста программы из исполняемого модуля.

    Наиболее интересный раздел на сайте — Библиотека. В нем вы найдете фрагменты из лучших книг по программированию.

    В разделе Статьи вы найдете небольшие материалы разных авторов по программированию и компьютерной технике.

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

    Другие страницы ссылок на assembler.ru:

  • рунет : win32asm

    ассемблер


  • internet : win32asm

    assembler


  • избранное : форумы

    документация

    разное



  • Ссылки


    рунет
    win32asm - программирование на ассемблере для Windows (8)

    ассемблер - программирование на ассемблере (14)

    братские сайты - сайты настоящих ассемблерщиков, с которыми у нас установились теплые дружеские отношения (3)
    internet
    win32asm - программирование на ассемблере для Windows (3)

    assembler - программирование на ассемблере (18)

    избранное
    форумы - конференции, форумы, рассылки, эхи из Usenet, Internet, Рунет, Fidonet (8)

    документы - книги, документация, статьи (6)

    разное - интересные ресурсы, имеющие косвенное отношение к программированию на ассемблере (1)



    Статьи по Assembler

    Артефакты


    В таком контексте - интересные программы, сделанные неизвестно для чего - впервые применил термин "артефакт" Serrgio в соответствующем разделе братского нам Сайта изобретателей фонариков на солнечных батарейках. Нам это название показалось настолько удачным, что мы тоже применили его, не задумываясь. Надеемся, что вам оно также понравится, и вы посвятите всю свою жизнь созданию артефактов и закончите ее в нищете, но посмертно будете признаны великим программистом.
    Само слово "артефакт", придуманное неизвестно кем неизвестно в какие времена и означающее "сделанный искусственно", ввел в современный обиход, скорее всего, Артур Кларк в своей великой "Одиссее-2001". Там, если помните, так назывался черный обелиск - носитель внеземного мессиджа и одновременно средство для превращения Юпитера в еще одно солнце. А может быть, это был Клиффорд Саймак, но у него столько великих книг, что мы не припомним, в которой это случилось. Как бы там ни было, потом слово размножилось на страницах тысяч фантастических романов и навязло в зубах.
    Возможно поэтому, когда 2001 год оказался не за горами, а полет к Юпитеру по-прежнему остался уделом фантастики, словечку легко нашлось новое применение. Теперь им обозначают всякие полезные штучки, от куска колбасы до трансцендентирующего суперплазмогана, находимые геймерами в лабиринтах бесчисленных шутеров. Наверное, у этого применения термина тоже есть конкретный автор, но мы его не знаем.
    салют
  • Скачайте программу salute.com (2277 байт).
  • Если доверяете, переходите сразу к пункту 3. Если не совсем доверяете, проверьте ее каким-нибудь антивирусом. Если совсем не доверяете, то купите себе новый компьютер, и переходите к пункту 3, пользуясь старым.
  • Запустите программу.
  • Немножко испугайтесь, увидев черный экран.
  • Нажмите клавишу пробел.
  • Еще раз испугайтесь, услышав треск со стороны системного блока.
  • Успокойтесь, потому что это трещит не винчестер, а спикер. Это такое звуковое сопровождение. В поры написания этой программы саундбластеров еще в помине не было.
  • Насладитесь зрелищем.
  • Нажмите пробел и удерживайте.
  • Насладитесь зрелищем.
  • Нажмите клавишу Esc.
  • Плюньте и сотрите программу.


  • цветочки

    Это технологическая версия предыдущей программы - на ней отлаживались траектории движения огней. Но поскольку она имеет кое-какую самостоятельную красоту, мы ее включили сюда отдельным пунктом.

  • Скачайте программу flowers.com (1912 байт).
  • Выполните п.2 из предыдущей инструкции.
  • Выполните п.3 из предыдущей инструкции.
  • Выполните п.4 из предыдущей инструкции.
  • Выполните п.5 из предыдущей инструкции.
  • Пропустите п.п.6 и 7 из предыдущей инструкции - в этой программе нет звука.
  • Далее последовательно выполняйте п.п.8...12 из предыдущей инструкции.


  • галактика

  • Скачайте программу galaxy.com (2194 байта).
  • Запустите ее.
  • Наслаждайтесь.
  • Понажимайте клавишу пробел.
  • Когда надоест - нажмите Esc.


  • Эта программа может служить тестом вашей способности к ментальной связи с компьютером, то есть, грубо говоря, позволяет определять, дайвер вы или нет (© С.Лукьяненко). Для этого:

  • долго-долго смотрите на картинку
  • попытайтесь представить ее раскрашенной в один из 14-ти цветов палитры EGA (кроме белого и черного)
  • нажмите клавишу пробел
  • если угадали - поставьте себе плюсик, а если нет - минусик.
  • повторите предыдущие пункты 1000 раз


  • Если полученное вами число плюсиков больше 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 в части, используемой при выполнении соединения. При этом имеют место следующие преимущества и дополнительные возможности:
  • пользователь может выбрать желаемое соединение (провайдера) из нескольких заранее подготовленных
  • для выбранного соединения пользователь может выбрать желаемый телефонный номер из нескольких заранее подготовленных
  • для выбранного соединения пользователь может выбрать желаемую пару логин/пароль из нескольких заранее подготовленных. При этом пароль не сохраняется в стандартном файле паролей Windows
  • выбор осуществляется простым образом - с помощью выпадающих списков. В каждом списке может быть до 16 позиций
  • последнее состояние выбора сохраняется между сеансами работы приложения в автоматически создаваемом файле mycall.ini, помещаемом в рабочий каталог приложения
  • процесс установления соединения контролируется с помощью стандартных текстовых сообщений RAS, выводимых в строку статуса.
  • при неудаче установления соединения автоматически выполняется повторная попытка (автодозвон). Попытки продолжаются до тех пор, пока соединение не будет установлено, либо до прерывания пользователем
  • при разрыве установленного соединения по команде пользователя или при потере связи автодозвон не выполняется, приложение переходит в режим ожидания команд пользователя
  • окно приложения компактно, имеет фиксированный размер и занимает мало места на рабочем столе. Положение окна сохраняется между сеансами работы приложения


  • Кроме того, приложение MyCall имеет следующие особенности:

  • не требует инсталляции. Достаточно поместить файлы приложения в какой-нибудь каталог, и оно сразу становиться работоспособным. Приложение не выполняет никаких записей в реестр Windows или куда-либо еще, кроме своего рабочего каталога
  • приложение чрезвычайно компактно. Для его работы необходимо иметь всего два файла - текстовый файл настроек mycall.txt и исполняемый файл mycall.exe. Размер исполняемого файла немногим более 8,5 Кбайт
  • все необходимые данные для работы приложения (списки провайдеров, телефонов и логинов) заносятся пользователем при установке приложения в файл настроек mycall.txt с помощью любого текстового редактора. Формат файла настроек описан в прилагаемой документации
  • приложение сопровождается документацией в формате HTML на русском языке


  • Поскольку приложение MyCall разрабатывалось как учебное, в нем, с целью сохранения компактности, не реализованы некоторые полезные возможности:

  • отсутствует контроль и выдача пользователю сообщений об ошибках RAS. В результате пользователь не будет знать, например, о несовпадении пароля; для него эта ситуация будет выглядеть как неудача соединения с последующим повтором попытки
  • применен жесткий позиционный формат файла настроек mycall.txt. Контроль его соблюдения отсутствует. Возможные ошибки пользователя при составлении файла настроек могут быть обнаружены только по некорректному содержанию списков выбора после запуска приложения
  • отсутствует оповещение о факте установления соединения, кроме текстового сообщения в строке статуса. Пользователь вынужден контролировать процесс установления соединения визуально
  • управление приложением осуществляется только с помощью мыши, клавиатура не используется


  • ВНИМАНИЕ! Логины и пароли хранятся в файле mycall.txt в открытом виде. Будьте осторожны и не применяйте приложение MyCall в ситуациях, когда доступ к файлу mycall.txt могут получить лица, способные воспользоваться им ненадлежащим образом. Например, не рекомендуется использовать MyCall на чужом компьютере, компьютере коллективного пользования или офисном.

    Несмотря на указанные недостатки, двухмесячная (на момент публикации) постоянная эксплуатация приложения MyCall показала его достаточную функциональность. Приложение полезно в ситуациях, когда пользователь имеет активный доступ в Интернет через нескольких провайдеров, с несколькими учетными данными, через несколько разных телефонных номеров модемных пулов.

    Приложение поставляется в виде zip-файла mycalla.zip размером 11471 байт. Для его установки необходимо:

  • распаковать zip-файл в подготовленный каталог
  • изучить прилагаемую документацию mycalla.htm
  • отредактировать, как указано в документации, файл настроек mycall.txt
  • выполнить пробные сеансы связи для проверки правильности составления файла настроек



  • Mycall (c++)


    Эта программа разработана в рамках проекта assembler.ru для демонстрации различий между приложением win32, написанным на C++, и аналогичным приложением написанным на ассемблере.
    См. полный исходный текст обоих вариантов приложения, комментарии к нему и анализ различий.
    Copyright © 2000 Авторские права защищены и распространяются на исполняемый модуль, исходный текст и документацию. Разрешено свободное распространение. Распространение на возмездной основе любого вида запрещено. Любая модификация разрешена только в целях личного пользования. Распространение в модифицированном виде запрещено. Распространение исполняемого модуля без документации запрещено. Пользователь применяет программу под собственную ответственность. Автор не несет ответственности за проблемы, которые могут возникнуть у пользователя в связи с применением программы.
    Будем благодарны, если об ошибках и недостатках, а также замечания и предложения вы сообщите по адресам email, указанным в заголовочной части документа.
    Приложение MyCall заменяет штатный интерфейс "Удаленный доступ к сети" (Remote Access Service, RAS) Windows 95/98 в части, используемой при выполнении соединения. При этом имеют место следующие преимущества и дополнительные возможности:
  • пользователь может выбрать желаемое соединение (провайдера) из нескольких заранее подготовленных
  • для выбранного соединения пользователь может выбрать желаемый телефонный номер из нескольких заранее подготовленных
  • для выбранного соединения пользователь может выбрать желаемую пару логин/пароль из нескольких заранее подготовленных. При этом пароль не сохраняется в стандартном файле паролей Windows
  • выбор осуществляется простым образом - с помощью выпадающих списков. В каждом списке может быть до 16 позиций
  • последнее состояние выбора сохраняется между сеансами работы приложения в автоматически создаваемом файле mycall.ini, помещаемом в рабочий каталог приложения
  • процесс установления соединения контролируется с помощью стандартных текстовых сообщений RAS, выводимых в строку статуса.
  • при неудаче установления соединения автоматически выполняется повторная попытка (автодозвон). Попытки продолжаются до тех пор, пока соединение не будет установлено, либо до прерывания пользователем
  • при разрыве установленного соединения по команде пользователя или при потере связи автодозвон не выполняется, приложение переходит в режим ожидания команд пользователя
  • окно приложения компактно, имеет фиксированный размер и занимает мало места на рабочем столе. Положение окна сохраняется между сеансами работы приложения


  • Кроме того, приложение MyCall имеет следующие особенности:

  • не требует инсталляции. Достаточно поместить файлы приложения в какой-нибудь каталог, и оно сразу становиться работоспособным. Приложение не выполняет никаких записей в реестр Windows или куда-либо еще, кроме своего рабочего каталога
  • приложение чрезвычайно компактно. Для его работы необходимо иметь всего два файла - текстовый файл настроек mycall.txt и исполняемый файл mycall.exe. Размер исполняемого файла немногим более 8,5 Кбайт
  • все необходимые данные для работы приложения (списки провайдеров, телефонов и логинов) заносятся пользователем при установке приложения в файл настроек mycall.txt с помощью любого текстового редактора. Формат файла настроек описан в прилагаемой документации
  • приложение сопровождается документацией в формате HTML на русском языке


  • Поскольку приложение MyCall разрабатывалось как учебное, в нем, с целью сохранения компактности, не реализованы некоторые полезные возможности:

  • отсутствует контроль и выдача пользователю сообщений об ошибках RAS. В результате пользователь не будет знать, например, о несовпадении пароля; для него эта ситуация будет выглядеть как неудача соединения с последующим повтором попытки
  • применен жесткий позиционный формат файла настроек mycall.txt. Контроль его соблюдения отсутствует. Возможные ошибки пользователя при составлении файла настроек могут быть обнаружены только по некорректному содержанию списков выбора после запуска приложения
  • отсутствует оповещение о факте установления соединения, кроме текстового сообщения в строке статуса. Пользователь вынужден контролировать процесс установления соединения визуально
  • управление приложением осуществляется только с помощью мыши, клавиатура не используется


  • ВНИМАНИЕ! Логины и пароли хранятся в файле mycall.txt в открытом виде. Будьте осторожны и не применяйте приложение MyCall в ситуациях, когда доступ к файлу mycall.txt могут получить лица, способные воспользоваться им ненадлежащим образом. Например, не рекомендуется использовать MyCall на чужом компьютере, компьютере коллективного пользования или офисном.

    Несмотря на указанные недостатки, двухмесячная (на момент публикации) постоянная эксплуатация приложения MyCall показала его достаточную функциональность. Приложение полезно в ситуациях, когда пользователь имеет активный доступ в Интернет через нескольких провайдеров, с несколькими учетными данными, через несколько разных телефонных номеров модемных пулов.

    Приложение поставляется в виде zip-файла mycallc.zip размером 11569 байт. Для его установки необходимо:

  • распаковать zip-файл в подготовленный каталог
  • изучить прилагаемую документацию mycallc.htm
  • отредактировать, как указано в документации, файл настроек mycall.txt
  • выполнить пробные сеансы связи для проверки правильности составления файла настроек



  • Пакет logcount


    Программный пакет LOGCOUNT - это набор программных средств для счета, учета и анализа посещаемости www-сайтов. Пакет прост в установке и использовании. Требования к уровню подготовки пользователя при работе с пакетом минимальны. Программы, входящие в состав пакета, компактны, быстры и занимают минимум ресурсов www-сервера и рабочей станции пользователя.
    Пакет ориентирован на применение на небольших корпоративных и персональных сайтах.
    Пакет logcount


    Пакет logcount


    С примерами использования пакета LOGCOUNT вы можете ознакомиться на сайтах:
  • www.lfnclub.wallst.ru - Клуб поклонников сериала La Femme Nikita
  • www.cssfund.ru - Центр анализа динамики стратегических отношений
  • www.salon-sk.com - Салон - Строительная Компания
  • www.bpks.dax.ru - Ассоциация "Балтпромкомплект", г.Санкт-Петербург
  • www.defsoft.ru - Defsoft - сборник программного обеспечения
  • www.elexpro.ru - ElexPRO - Компания - разработчик электроники
  • www.avt.com.ru - ЗАО АВТОМАТИКА-СЕВЕР, г.Санкт-Петербург
  • www.cradlefields.com - Cradle Fields, Inc, разработчики программного обеспечения для изучения математики и математических исследований
  • www.plastpolymer.h1.ru - Лаборатория термопластичных фторполимеров "Пластполимер"
  • www.bilous.idknet.com - Орден Залізної Остроги

  • Чтобы воспользоваться пакетом, скачайте файл 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>

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


  • даты и времени посещения с точностью до секунд
  • уникального идентификатора посещаемой страницы
  • ip-адреса посетителя
  • данных о рабочей среде посетителя (агент, операционная система, броузер)
  • данных о реферрере (адресе, с которого пришел посетитель)


  • Выполнять преобразование лог-файлов на рабочей станции пользователя в текстовый формат, пригодный для чтения и импорта в текстовые редакторы и базы данных, в том числе с использованием выборок:


  • по дате/времени посещения
  • по страницам сайта
  • по ip-адресам посетителей


  • Выполнять статистический анализ посещаемости, в том числе:


  • с выборкой по дате/времени посещения
  • с составлением рейтинга посещаемости страниц
  • с составлением рейтинга реферреров
  • с составлением рейтинга конфигураций рабочей среды посетителя
  • с составлением рейтинга ip-адресов посетителей


  • Читать запросные части URL реферреров (после символа "?"), в том числе:


  • запросы к поисковым машинам, с помощью которых посетители пришли на сайт
  • параметры вызова CGI-страниц, содержащих ссылки на сайт


  • Выполнять преобразование лог-файлов в расширенный формат www-сервера Apache/NCSA (combined/extended NCSA format) с целью их последующей обработки с помощью анализаторов других производителей, например:

    - WebTrends Log Analyzer

    - Analog

    - 10-Strike Log-Analyzer

    и других.
  • Редактировать лог-файлы.

    состав пакета

    Пакет LOGCOUNT поставляется в виде упакованного zip-файла logcount.zip (


  • log.pl - CGI-скрипт на языке Perl. Обеспечивает работу счетчиков и формирование лог-файлов.

    Размещается на www-сервере в каталоге cgi-bin.



  • log-rprt.pl - CGI-скрипт на языке Perl. Обеспечивает контроль текущего состояния посещаемости (число посещений, число посетителей, число посетителей в текущий момент, размеры лог-файлов) и прием лог-файлов. Прием осуществляется по протоколу HTTP в виде одного транспортного lct-файла



    Размещается на www-сервере в каталоге cgi-bin.



  • log-rqst.htm - HTML-документ, обеспечивающий безопасный вызов скрипта log-rprt.pl

    Размещается на рабочем месте пользователя.



  • log0unpk.exe - утилита распаковки транспортных lct-файлов. Извлекает из файлов, полученных с помощью скрипта log-rprt.pl, лог-файлы, которые в дальнейшем могут обрабатываться остальными утилитами пакета.

    Размещается на рабочем месте пользователя.



  • log1read.exe - утилита для преобразования лог-файлов в текстовый формат, пригодный для чтения и импорта в текстовые процессоры и базы данных

    Размещается на рабочем месте пользователя.



  • log2stat.exe - утилита для статистического анализа посещаемости на основании данных лог-файлов. Выполняет обобщенную статистическую обработку, а также формирует рейтинги страниц, ip-адресов, агентов и реферреров

    Размещается на рабочем месте пользователя.



  • log3srch.exe - утилита для преобразования запросных частей URL в текстовый формат, пригодный для чтения. Формирует упорядоченный по времени текстовый листинг посещений с указанием времени посещения, посещенной страницы, ip-адреса, агента и реферрера

    Размещается на рабочем месте пользователя.



  • log4ncsa.exe - утилита для преобразования лог-файлов в формат NCSA, позволяющий проводить анализ посещаемости с помощью лог-анализаторов сторонних производителей

    Размещается на рабочем месте пользователя.



  • log5edit.exe - утилита для редактирования лог-файлов

    Размещается на рабочем месте пользователя.



  • log-manu.chm - инструкция по установке пакета и работе с входящими в его состав программами
  • (sample).lct - пример, демонстрирующий возможности утилит пакета
  • click.txt - файл с начальным состоянием обобщенных счетчиков.
  • pages.txt - пример файла счетчиков страниц.
  • pagename.txt - пример файла комментариев (названий) к счетчикам страниц.
  • log-test.htm - тестовый документ для проверки работы счетчиков.

    системные требования

    Единственное требование для работы скрипта log.pl - это возможность установки и исполнения пользовательских скриптов на языке Perl на www-сервере.



    Операционная среда сервера значения, как правило, не имеет. Скрипт показал работоспособность на серверах под управлением ОС Windows NT 4.0 (Internet Information Server) и Unix (Apache).

    Для работы пакета не требуется использование директив включения на стороне сервера (SSI, Server Side Includes). Этим он отличается в лучшую сторону от большинства других текстовых счетчиков, так как существенно снижается нагрузка на сервер.

    Exe-программы, входящие в состав пакета - это консольные приложения Win32. Они работоспособны в операционных системах:


  • Windows 95/98/Me
  • Windows NT 4.0/2000

    технические характеристики

    Скрипт log.pl обеспечивает минимальную загрузку сервера при своем исполнении. Измеренная на непрерывном потоке запросов, она по времени занятия процессора всего в два раза выше, чем у "пустого" скрипта, не имеющего в своем составе ни одной команды.

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

    Тестирование скрипта, проведенное на маломощном сервере (Pentium-200, Windows NT 4.0, RAM 48 Mбайт, IIS) при отсутствии сторонней нагрузки, показало, что он обслуживает без потерь поток до 80 запросов в секунду. В реальных условиях этот показатель может существенно отличаться как в одну, так и в другую сторону. Однако можно быть уверенным, что скрипт обеспечит обработку посещаемости до нескольких тысяч или десятков тысяч хитов в сутки, что в большинстве случаев достаточно для персональных сайтов и сайтов для малого бизнеса.

    Скрипт выдает информацию для счетчиков в виде значений переменных языка JavaScript, доступных для обработки в документах HTML, представляющих страницы сайта. Благодаря этому веб-мастер может обеспечить показ значений счетчиков на страницах в текстовом виде, применяя для их оформления любые средства HTML и CSS. Опытный веб-мастер может также использовать эти значения для создания графических счетчиков с раздельным представлением разрядов и сравнительных диаграмм.



    Показания счетчиков, поддерживаемых скриптом, имеют смысл:


  • обобщенный счетчик посещений. Представляет суммарное количество хитов всех включенных в обсчет страниц сайта, накопленное с момента начала работы скрипта
  • обобщенный счетчик посетителей. Представляет суммарное количество посетителей, выполнивших хиты включенных в обсчет страниц сайта, накопленное с момента начала работы скрипта. Посетителем считается хост, выполнивший хит страницы сайта при условии, что этот хост не выполнял хитов в течение последних 30 минут
  • счетчик посетителей, находящихся на сайте в текущий момент. Представляет количество уникальных хостов, выполнивших хит любой включенной в обсчет страницы сайта в течение последних 10 минут
  • счетчики посещений и посетителей за текущие сутки, неделю, месяц и год. Значения счетчиков обнуляются в момент изменения соответствующей календарной даты по локальному времени сервера. С помощью специальной опции утилит обработки даты могут быть приведены к локальному времени пользователя или к Гринвичскому времени
  • счетчики посещений отдельных страниц. Представляют суммарное количество хитов каждой заданной веб-мастером страницы сайта, накопленное с момента начала работы скрипта. Счетчик может отображаться как на этой же странице сайта, так и на других страницах, в том числе и не включенных в обсчет. Таким образом могут формироваться списки посещаемости страниц сайта, например, для показа посетителям в составе оглавления

    Здесь: хит - загрузка страницы в броузер для просмотра пользователем, хост - компьютер пользователя, с которого осуществляется просмотр сайта. Характеризуется уникальным 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 году, еще до второго путча. Пользы в них столько же, сколько оказалось в итоге у всех этих долгих лет: интересно, но бессмысленно.




    Программы

    Программы

    Программы



    

        Программирование: Языки - Технологии - Разработка