Ассемблер?
Это просто! Учимся
программировать Выпуск N 021 (Оболочка) |
Сегодня в рассылке:
Дорогие мои подписчики! Нас уже очень много (более 8200 человек). Народ постоянно подписывается. Естественно, что мне одному становится тяжело вести нашу рассылку. В связи с этим я хотел бы навести небольшой порядок с тем, что у нас происходит, а также хочу попросить помощи у энтузиастов.
С момента прошлого выпуска произошли многие перемены. Прошу вас, дорогие мои, внимательно прочитать весь данный раздел.
Итак, начнем по порядку.
___________
1. Приглашаются помощники:
Если у вас есть время и возможность, то, пожалуйста, напишите мне. Буду рад рассмотреть ваши предложения. Более подробную информацию можно получить в письме, которое следует направить по адресу Assembler@Kalashnikoff.ru. В теле письма, пожалуйста, укажите, чем вы можете помочь. Буду очень признателен...
___________
2. Требуются ведущие рубрики "Ваши письма".
Необходимо качественно перевести письма подписчиков в формат HTML из текстового файла и оформить это соответствующим образом (как во всех наших выпусках оформлялась данная рубрика). Что я могу предложить? Предоставить вам полное ведение рубрики "Ваши письма" (с моими поправками, если понадобится). Ваше имя, e-mail, URL будут указаны в рассылке. Письма подписчиков будут высылаться вам по почте от моего имени, но без указания электронных адресов подписчиков. Ваша задача: отобрать наиболее интересные для подписчиков письма, исправить по мере необходимости орфографические ошибки, оформить письма в формате HTML и выслать мне готовый вариант до выхода следующего номера рассылки (примерно за 2-5 дней). Вот, собственно, и все!
___________
3. Экспертные группы (следует внимательно прочесть экспертам и подписчикам, задающим вопросы!).
К сожалению, мне пришлось закрыть три экспертные группы в связи с тем, что данным экспертам практически не приходит вопросов. Закрыты следующие группы:
Как показало время, большинство вопросов задается группе "Эксперты по общим вопросам программирования на Ассемблере под DOS". Безусловно, лидирует по количеству отвеченных вопросов эксперт Slava V. Четкие ответы, лаконичное содержание, простой стиль изложения, оперативность ответов - все это делает Slav'у незаменимым помощником для меня и для всех подписчиков. Те из вас, кто получил ответ от Slav'ы, подтвердят мои слова. Как ему это удается - ума не приложу! В связи с этим хочу повторно выразить благодарность Slav'е за оказанную помощь как мне, так и всем вам, дорогие подписчики.
Однако, кто-то из экспертов старается изо всех сил отвечать на ваши вопросы, а кто-то зарегистрировался и за все существование экспертных групп не ответил ни на один вопрос! Это особенно относится к указанной выше группе. Экспертов в данный момент довольно-таки много. Отбирать вручную и искать "сачков" нет времени. Поэтому мне придется поступить не очень хорошо, попросив вас, уважаемые эксперты, пройти т.н. перерегистрацию. Для этого необходимо направить мне пустое письмо до 31 января 2001 года включительно по адресу: Registration@Kalashnikoff.ru, указав в теле письма ваш электронный адрес по которому вы зарегистрированы в экспертной группе. В ответ на ваше письмо придут новые правила работы экспертных групп. Эксперты, от которых я НЕ получу писем до указанной даты, исключаются из числа экспертов. Сурово? Зато на мой взгляд справедливо по отношению к тем подписчикам, которые хотели бы стать экспертами, да группы уже укомплектованы, и мне с горечью в сердце приходится отказывать всем желающим. В последующих выпусках я напишу о том, сколько мест освободилось в той или иной группе. Надеюсь, что со временем мы соберем отличную экспертную группу в помощь вам, дорогие мои читатели.
Подписчики, желающие задать вопросы экспертам, должны ознакомиться с новыми правилами (установленными 15 января 2001 года), которые можно найти на нашем сайте. Если у вас проблемы с Интернетом, то напишите мне письмо по адресу AssmExperts@Kalashnikoff.ru. Данная информация будет выслана вам в течение двух рабочих дней. Подписчикам, получившим или ознакомившимся на сайте с новыми правилами и адресами экспертов после 15.01.2001, волноваться не стоит! Данные правила упорядочивают работу экспертных групп, а также вносят определенные требования для подписчиков задающих вопросы, с тем, чтобы у нас не возник хаос, беспорядок и пр. Я также постарался оградить экспертов от вопросов, которые задают "ленивые" подписчики, т.е. те, кто столкнувшись с "малюпасенькой" проблемкой, сразу же задает вопрос экспертам. Дорогие мои! Так ведь не интересно! Уверяю вас, что пасуя перед сложностями (или обращаясь с мелкой проблемой к помощникам), вы мало чего добьетесь как в изучении Ассемблера, так и во всем остальном. Постарайтесь приложить максимум усилий для решения возникшей проблемы. Если же ничего не выходит, то - пожалуйста. Эксперты с огромным удовольствием ответят на ваш вопрос(ы). По содержимому вопроса ведь сразу видно, думали вы или нет, прежде, чем написали экспертам...
Все вопросы, поступившие в адрес экспертов, но противоречащие новым правилам, игнорируются экспертами!
___________
4. Обсуждение книги.
Что можно сказать по этому поводу. Поступило ко мне несколько отзывов и не более того. Причем, все были положительные (к моему великому удивлению), что воодушевило меня на продолжение осуществления моих сумасшедших планов. Я так понимаю, что книга, которая будет издана (по крайней мере, я надеюсь на это) нужна многим, что, безусловно, радует.
Тем не менее, нет необходимости создавать отдельный форум (группу обсуждений) для такого пустяка. Если у вас есть что сказать, то оставляйте запись в Гостевой книге или на форуме. Буду рад прочитать ваши рассуждения не только я один, но (надеюсь!) и многие подписчики.
___________
5. Новые поступления на сайте.
На нашем сайте (http://www.Kalashnikoff.ru) теперь можно найти документацию на русском языке по работе с SoftIce. Берите и качайте на здоровье!
Более того, теперь доступно пособие по написанию вирусов на русском языке, причем, в довольно-таки понятном и простом для начинающего программиста изложении. В данном пособии описаны несколько вариантов вирусов (включая резидентные вирусы и вирусы, заражающие *.exe-файлы). Надеюсь, что вы почерпнете из этого файла много действительно полезной и нужной информации, которая поможет вам освоить все тонкости программирования на Ассемблере.
___________
6. Смена внешнего вида сайта.
Ничто не стоит на месте. Все находится в движении, меняется. Вот и сайт наш уже третий раз поменял свой облик (условно - Версия 3.0). Сидел я, мучил свою несчастную, убитую горем "четверку" все выходные. Зато теперь плод моих трудов вы можете оценить лично. Был бы безмерно счастлив, если бы вы оставили запись (ваше мнение) в Гостевой книге.
Конечно, пока что еще не все подразделы сведены к одному дизайну, стилю (например, Гостевая и форум). Но, тем не менее, уже можно сделать однозначные выводы.
Я же, в свою очередь, хотел бы выразить глубокую благодарность за качественный хостинг компании AGAVA и лично Бобу Марлину (BoB MaRLiN) за помощь в создании настоящей Гостевой книги и выделенное место под нее.
Если у вас есть время и желание написать форум и (или) гостевую на нашем сайте, то буду вам безмерно благодарен. Естественно, все ваши права будут соблюдены. Мой провайдер (www.100mb.ru) позволяет создавать свои CGI-скрипты в каталоге cgi-bin. Если вы надумали написать что-либо, то не нужно мне слать письмо с предложениями. Просто сделайте и пришлите (если не сложно). Я все выложу на сайт. Главное условие - чтобы форум и гостевая были одного стиля с сайтом. Очень на вас надеюсь...
Да! Чуть не забыл! Теперь статистику посещений нашего сайта можно найти здесь: http://www.Kalashnikoff.ru/usage. Она обновляется ежедневно. Статистика включает в себя трафик, страны и регионы, жители которых заходят на сайт, посещаемость по часам и многое другое. Зачем это? Да так, вдруг кому интересно будет...
И вот еще: установил на сайте кнопку, которая сигнализирует всем посетителям о том, в Сети ли я сейчас или нет. Мне понравилось это дело... Только что-то "глючит" она иногда...
Дорогие мои подписчики! Ваши письма мы рассмотрим в следующем спецвыпуске, который выйдет до 26 января 2001 года. Дело в том, что писем ваших очень много, и большинство из них будут интересны всем подписчикам рассылки. Только вот байты сейчас не позволяют сделать это... Так что подождите, пожалуйста, спецвыпуск 022.
Как вы уже знаете, наша оболочка разбита на несколько файлов. Ничего лучшего, чем разделить эти файлы по разным окошкам, я не придумал. Надеюсь, что вы без труда разберетесь. Как скопировать эти файлы в DOS-формат - я писал в предыдущих выпусках.
Тем не менее, если вам лень копировать каждый файл в DOS-формат, то вы можете перекачать все целиком отсюда: http://www.Kalashnikoff.ru/Assembler/Issues/Enclosures/Sshell21.rar.
Sshell21.asm |
|
Display.asm |
Files.asm |
Keyboard.asm |
Main.asm |
|
Data.asm |
Итак, поехали...
Если вы уже запустили нашу "оболочку", то ничего особенно интересного вы, скорее всего, не нашли. Мы просто читаем содержимое текущего каталога в память и выводим первые двадцать найденных файлов на экран.
Однако, присмотревшись, вы поймете, что наша оболочка проделывает уйму работы. А именно:
_______
1. Ищет первый файл;
2. Если это '.', то см. пункт 4; *
3. Заносит в память имя найденного файла;
4. Ищет следующий файл;
5. Заносит имя найденного файла в память;
6. Файлы закончились?
7. Нет - см. пункт 4.
8. Да - начинает выводить файлы на экран.
9. Выведено уже 20 файлов? Нет - см. пункт 8. Да - см. пункт 10.
10. Ждет нажатия на клавишу.
11. Пользователь нажал ESC? Да - см. пункт 12. Нет - см. пункт 10.
12. Запрашивает пользователя: уверен ли он, что хочет выйти в DOS.
13. Не уверен (т.е. нажал все, что угодно, кроме 'Y' или 'y') - см. пункт 10.
14. Уверен (т.е. нажал 'Y' или 'y') - выходит в DOS.
Вот, собственно, и алгоритм работы нашей оболочки.
* Примечание. В любом подкаталоге первый файл всегда '.' . Точнее, это не файл, а каталог. Если в командной строке набрать 'CD .' , то директория не изменится. Т.е. данный каталог - это просто текущий каталог. Нам его не нужно выводить в оболочке. Команда DIR выводить эту директорию на экран, в отличие от оболочек.
Теперь подробнее...
_______
Новшество первое.
Обратите внимание, как мы вызываем процедуру Draw_frame (MAIN.ASM, Main_proc). На первый взгляд - ничего особенного. Но это только кажется... Раньше мы заносили в стек адреса строк, которые будут выводится вверху и внизу нашей рамки, а также их атрибуты. Теперь мы атрибуты в стек не заносим. Но как же тогда сообщения выводятся в нужном цвете? Обратите внимание, что находится перед строками Mess_head и Mess_down:
Mess_head db 1Eh, ' Super Shell, Версия
1.0 ',0
Mess_down db 1Dh, ' Россия, Москва,
2001 ',0
Вот это и есть как раз нужные нам атрибуты соответствующих строк. Поступая таким образом, мы экономим байты нашей оболочки, более того, мы не нагружаем стек. Отпадает необходимость заносить в стек два слова атрибутов для верхней строки и два слова для нижней. Процедура вывода строк на экран (DISPLAY.ASM, Draw_messfr) перед тем, как выводить строку, занесет в AH первый символ, который и будет являться атрибутом! Это проще!
mov ah,[si] ;Первый символ
строки - это атрибут
inc si ;Следующий байт - начало
строки
call Count_strmid ;Вычисляем середину
строки
call Print_string ;Выводим строку на экран
_______
Новшество второе.
Теперь процедура рисования рамки (DISPLAY.ASM, Draw_frame) выводит еще и линию вверху окошка по нашему требованию. А что мы добавили перед вызовом данной процедуры? Да ничего почти! Просто изменили один бит:
push 10b ;Экран не копировать, но
вывести верхнюю линию.
call Draw_frame ;Рисуем рамку на весь
экран
Т.е. нулевой бит последнего заносимого слова в стек (если он включен (установлен)) указывает на то, следует ли копировать экран в буфер или нет. Второй бит - следует ли рисовать линии вверху окошка или нет.
Видите, как наша процедура совершенствуется на глазах. А это далеко не предел! Еще дальше пойдем - еще больше будет!
Внимание! "А как же нам проверить, включен ли нулевой бит или нет? Скорее всего, оператором CMP здесь не обойтись." Да, действительно, не обойтись. И вот почему.
Представьте, что первый раз мы выводим окошко, предварительно скопировав экран, но не выводим линию вверху. Тогда нулевой бит будет равен 1:
push 1
Следовательно, можно проверить так (в процедуре draw_frame данное слово будет называться Other (иное)):
cmp Other,1
Если флаг нуля устанавливается, т.е. Other = 1, то нужно предварительно скопировать экран в память.
Хорошо. Теперь представим, что нужно вывести окошко с линией вверху, но не копировать экран (т.е. первый бит будет равен единице, а нулевой - 0):
push 10b ;или push 2
...
cmp Other,2
И, наконец, представим, что нужно вывести и линию вверху, и сохранить экран:
push 11b ;или push 3
...
cmp Other,3
Вроде все нормально... Однако, тем не менее, существует проблема. Нулевой бит (т.е. следует ли копировать экран или нет) мы проверяем в начале процедуры Draw_frame (т.е. до того, как вывели окошко), а вывод верхней линии - после того, как экран скопирован (если нужно), и выведено окошко. Теперь подумайте: можно ли с помощью CMP проверить установлен ли бит нулевой, игнорируя (умышленно не замечая) все оставшиеся биты проверяемого слова (Other)? Вдумайтесь в вопрос. Не спешите читать дальше. Правда, подумайте! Возьмите листок бумаги и поэкспериментируйте! Если сами поймете что да как, то будет гораздо проще разобраться с новым оператором...
...
Не сомневаюсь, что вы нашли выход из положения. Например, так:
mov ax,Other ;Перенесем временно в AX
значение переменной Other
push ax
and ax,1 ;Аннулируем все биты, кроме
нулевого (вспомним логические
команды)
cmp ax,1 ;Проверим, равен ли теперь AX
1?
pop ax
mov Other,ax ;Восстановим переменную
Other
je Ravno ;Перейдем, если равен
Следует отметить, что операторы MOV и POP флаги (в частности, флаг нуля) не меняют!
Хорошо получилось! Или не очень? Вероятно, есть другие способы? Безусловно есть! Ассемблер такой гибкий язык, что может делать невероятные, нет - поистине невероятные вещи!
Чтобы временно не сохранять регистр или переменную перед проверкой одного-двух-трех... битов, используется оператор TEST:
Название | Перевод | Применение | Процессор |
TEST приемник, источник | Test - тест, проверка | Проверка одного и более битов | 8086 |
Примеры:
mov ax,10100001b
test ax,1 ;Проверим на то, равен ли
нулевой бит единице.
jnz Ravno ;Переход, если
равен
Обратите внимание, что после CMP мы ставим JE (JZ), если хотим перейти на определенную метку в случае, если приемник и источник равны. В команде TEST все идет "шиворот-навыворот". Т.е. JE (JZ) перейдут на метку, если приемник и источник НЕ равны и наоборот: JNE (JNZ) перейдут на метку, если приемник и источник равны. Не надо путать!
Еще пример:
mov cl,100101b
test cl,1000b ;Проверим на то, равен ли
третий бит единице
jz Ne_ravno ;Переход, если НЕ
равен
Поехали дальше...
Мы остановились на том, что переменная Other будет содержать более одного параметра. Причем эти параметры должны отвечать на вопрос ДА или НЕТ, т.е. РАВНО или НЕ РАВНО. Как, например, в нашей процедуре Draw_frame. Повторю: первый бит отвечает за рисование линии вверху окошка, а нулевой - за копирование участка экрана перед выводом окошка. Вспомните окно подтверждения выхода из оболочки. Экран-то прежде копируется в память. А вдруг пользователь передумает выходить? Нам что тогда, заново рисовать основное окно и перечитывать каталоги? Или проще сохранить часть затираемого экрана, а затем моментально восстановить его? Да что я говорю?! Прошли мы уже это...
Использование битов переменной для подобных целей подходит как никогда лучше. Мы экономим байты. Вместо, скажем, 8 или 16 переменных мы используем всего одну! Красота...
Вот кусок из нашего файла-приложения (этим мы подведем черту с TESTом):
mov ax,Other ;Получим дополнительную
информацию
test al,1 ;Нулевой бит равен 0?
jz No_copyscr ;Если так, то копировать
экран не нужно.
___________
Основы работы с памятью в DOS.
Сразу отмечу, что пока мы будем работать с основной памятью, т.е. с 640 Кб.
Подробно рассмотреть управление памятью в DOS за один выпуск невозможно. Мы сегодня затронем лишь ключевые, основополагающие моменты.
Как только программа загрузилась, DOS автоматически отводит для нее всю свободную память. Программист может по своему усмотрению урезать блоки памяти, отводить другие (сколько памяти хватит), а также освобождать отведенные участки (блоки) памяти.
Зачем это нужно?
Представьте ситуацию. Мы написали программу, которая, в свою очередь, загружает другую. Но так как вся память изначально выделена только нашей программе, то мы не сможем загрузить никакую программу из нашей. Ведь ей тоже нужно некоторое количество памяти?
Для того, чтобы урезать память, используется функция 4Ah прерывания 21h:
Вход: | AH=4Ah ES=сегмент распределенного блока BX=размер блока в 16-и байтовых параграфах |
Выход: | JC -
ошибка, при этом: AX-код ошибки |
Распределенный блок в нашем случае - вся память, отведенная программе, начиная от нулевого смещения сегмента CS и заканчивая последним свободным байтом. Поэтому перед вызовом функции 4Ah следует убедиться в том, что ES указывает на сегмент, куда мы загрузились (см. таблицу выше)!
Вот какая ситуация после загрузки нашей программы в память не будем брать адреса; какая разница?):
1. Системные файлы; --- память занята от 0 до текущего байта
2. Резидентные программы; --- память занята от 0 до текущего байта
3. Наша программа; --- память занята от 0 до текущего байта
4. Метка Finish; --- память занята от 0 до текущего байта
5. Отведенная память нашей программе. --- память занята от 0 до конца.
Получается, что ВСЯ память (640 Кб) занята после того, как программа загрузилась. Наша задача - урезать занятую нашей программой память до метки Finish (см. п. 3). Для чего это нужно - рассмотрим позже.
Обратите внимание, как мы ужимаем существующий блок памяти (MAIN.ASM, Prepare_memory):
mov bx,offset Finish ;BX=последний байт
нашей программы
shr bx,4 ;Т.к. BX должен содержать не
количество байт, а количество
блоков по 16 байт, то мы
должны сдвинуть биты вправо на 4
inc bx ;Увеличим BX на один (на всякий
случай!)
mov ah,4Ah ;Функция уменьшения/расширения
существующего блока памяти
int 21h ;В данном случае урезаем, т.к.
наша программа, естественно, меньше
отведенного блока памяти после
загрузки.
Теперь картина такая:
1. Системные файлы; --- память занята от 0 до текущего байта
2. Резидентные программы; --- память занята от 0 до текущего байта
3. Наша программа; --- память занята от 0 до текущего байта
4. Метка Finish; --- память занята от 0 до текущего байта
5. Память за меткой Finish. --- память свободна до конца 640 Килобайта, начиная с первого байта, следующего за меткой Finish!
Размер памяти примерно такой: 640Кб минус смещение метки Finish.
Теперь отведем кусок памяти размером 1000h 16-и байтовых блоков (параграфов) или 65536 байт:
mov ah,48h
mov bx,1000h
int 21h
mov Seg_files,ax
Функция 48h прерывания 21h - выделить блок памяти:
Вход: | AH=48h BX=размер блока в 16-и байтовых параграфах |
Выход: | JC -
ошибка, при этом: AX-код ошибки; Иначе: AX=сегмент выделенного блока. |
Думаю, что объяснять приведенный над таблицей пример не нужно. Все и так понятно. Рассмотрим только, что получилось:
1. Системные файлы; --- память занята от 0 до текущего байта
2. Резидентные программы; --- память занята от 0 до текущего байта
3. Наша программа; --- память занята от 0 до текущего байта
4. Метка Finish; --- память занята от 0 до текущего байта
5. Память за меткой Finish + 65536 байт; --- память занята
6. Память, начиная с адреса Finish + 65537 байт --- свободна
Зачем мы выделили блок памяти размером 64Кб?
В эту память мы будем загружать информацию о найденных файлах в текущем каталоге. А как вы думаете NC показывает вам файлы в окошечке? Элементарно! Просто считывает их к себе в память, а затем с ними работает (точнее, вы работаете). Команда DIR, например, не загружает в память найденные файлы. А зачем? Она нашла файл, вывела информацию на экран и - следующий...
Подписчик (задумчиво): Я тут сейчас вот как бы нечайно подумал... Мне кажется, это так сложно все... Нет, наверное, это не для меня... Пусть другие пишут на Ассемблере, а я лучше на Бейсике... Сдался мне Асм... Калашников этот вечно лишние проблемы накручивает... Спал раньше так хорошо по ночам...
Ну и очень плохо, что вы спешите делать выводы, не попробовав даже написать простейшую программку. На ваш взгляд - это сложно. На самом деле - элементарщина! Не пугайтесь!!! Все самое интересное только начинается! Конечно, сложные и неинтересные вещи. Но это вам пока только кажется, что они нудные, и знать их не надо. На самом деле, в Ассемблере не бывает неинтересных вещей. Это сложно понять сразу. Со временем вы все выучите...
Как записываем в память найденные файлы?
Алгоритм простейший:
1. Ищем первый файл. Нет файлов - см. п. 5;
2. Заносим имя файла в отведенный блок памяти. Дописываем после имени файла нуль;
3. Ищем следующий файл. Нет больше файлов - см. п. 5;
4. Заносим следующий файл в память сразу же за найденным предыдущим. На пункт 3;
5. Заносим еще один нуль после последнего найденного файла для того, чтобы дать понять процедурам нашей оболочки, что это был последний найденный файл.
6. Выводим указанное количество файлов на экран (сколько находится в переменной Number_files) (FILES.ASM, Out_files).
Думаю, что труда разобраться с тонкостями процедур не составит, тем более, что в файле-приложении достаточно комментариев.
Удачного вам изучения!
До скорой встречи!
С уважением,
Автор
рассылки: Калашников Олег
URL сайта подписчиков: http://www.Kalashnikoff.ru
E-mail автора: Assembler@Kalashnikoff.ru
ICQ: 68951340
Москва, 2001.
(C) Авторское право принадлежит автору рассылки. Использование материала из рассылки в коммерческих и иных подобных целях, а также публичное публикование без письменного согласия автора влечет ответственность за нарушение авторских прав. |