Статьи по 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
@<mov esi,one_buffer>,<mov edi,another_buffer>,<mov ecx,how_many_bytes>,<cld>,<rep movsb>

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



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


Это первая часть статьи, она содержит постановку задачи. Реализация - во второй части.

Лептоны (от греч. 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 и пр. Если он вам понравится, вы можете использовать приведенный здесь код один к одному. Он вполне работоспособен и достаточно функционален. Но еще правильнее воспринимать его как набор идей, которые вы свободно можете развить и дополнить так, как посчитаете нужным. Или, например, перевести на другой язык программирования.

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


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


Это вторая часть статьи. Постановка задачи - в первой части.

В реализации задействованы файлы:

@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 <par0> mov esi,par0 ENDIF IFNB <par1> mov edi,par1 ENDIF IFNB <par2> mov edx,par2 ENDIF IFNB <par3> 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 <dispatcher_>,@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 <dispatcher_>,@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<B_BASE) ;если это молитва, а не бродкаст @if(eax) ;и если вызов обработан @break ;то завершить опрос диспетчеров @endif @endif add ebx,4 ;перейти к следующему диспетчеру @endw @endif ret supervisor ENDP

Пояснения:

Здесь и далее применена транскрипция структурных директив 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) вполне решаемы.


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


Ваш пол: муж. жен. другое

Сколько вам лет: 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









Что лучше: push es pop dx mov ax,es mov dx,ax Трудно сказать