Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Ассемблер? Это просто! Учимся программировать Выпуск ╧ 006


Служба Рассылок Городского Кота

Здравствуйте, уважаемые любители Ассемблера!


Выпуск 006

I like to move it, move it

I like to move it, move it

I like to... move it!

Напел я старенькую песню и

загрузил в AX число 20h


Сегодня в нашей рассылке:


Информация для вновь подписавшихся

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

www.subscribe.ru/archive/comp.prog.assembler

www.oleg77.newmail.ru/Assembler/For_assemblers.html

Напишите мне с просьбой выслать: oleg77@online.ru

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

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

Благодарю всех, кто подписался.


Ваши письма

На прошлой неделе я получил еще порцию писем от вас. Вот что мне хотелось бы отметить:

Дорогие мои! Не спешите! Я, к сожалению, не успеваю отвечать на ваши вопросы. Давайте все будем рассматривать по порядку. Я, конечно, понимаю, что вам не терпится попробовать ВСЕ. Я сам когда-то учился программировать. Руки так чесались, что по ночам лез на стенку. В то время (1993 год) у меня не было компьютера, и приходилось довольствоваться услугами компьютерных центров, в которых стояли тогда отечественные ЕС-1840 (жалкое подобие IBM PC/XT, только без винчестера)...

Пробуйте, эксперементируйте. Поверьте, самому гораздо интереснее докапываться до истины. У вас есть все необходимое (tasm, tlink, afd/CV). Если кто-то не имеет этих программ - возьмите здесь:

http://www.oleg77.newmail.ru/Assembler/For_assemblers.html

Если уж вы зашли в тупик, то пишите ( oleg77@online.ru?Subject=Я в тупике! ). Постараемся разобраться.


Результаты голосования.

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

Команды и инструкции 486-Pentium 17
Viewer для просмотра графики 9
Безобидный вирус 31
Графическая игра 12
Дизассемблер 7
Графическая заставка с музыкой 15
Локальная сеть 18
Оболочка типа Нортон Коммандер 29
Резидент 24

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

Как видите, первая тройка выглядит так:

1) Безобидный вирус;

2) Оболочка;

3) Резидент.

Что тут можно сказать? Очень сложно всем угодить, к сожалению. У меня есть два варианта:

1) Можно сделать рассылку чаще (2 раза в неделю), но меньшего объема. При этом, например, в понедельник рассматриваем Оболочку и команды 486, а в четверг Вирус и резидент.

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

Первый вариант, я думаю, предпочтительней. Пишите мне, если Вы не согласны:

oleg77@online.ru?Subject=Не согласен!

В письме, пожалуйста, укажите ваше мнение.

Когда мы будем писать Оболочку, то затронем Графическую заставку, возможно Viewer и прочее.

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


Стек/стэк (stack)

Немного теории.

Друзья мои! Я настоятельно рекомендую Вам выполнять все, что я вас прошу сделать в этом разделе (набирать программы, запускать их под отладчиком). Так вам будет гораздо легче разобраться со стеком.

У кого до сих пор нет отладчика, возьмите его, пожалуйста, здесь:

www.oleg77.newmail.ru/Assembler/For_assemblers.html

Итак, что же такое стек и для чего он нужен?

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

Давайте немного договоримся об определениях.

Пусть будет так, что сегмент "растет" сверху вниз:

0000

0001

0002

...

FFFE

FFFF

Т.е. мы как бы погружаемся под землю. Таким образом (сверху вниз) выполняется программа (если, конечно, не встречаются инструкции (команды) типа jmp, call и т.п.).

Стек же наоборот пополняется снизу вверх. Вершина стека - 0FFFFh, а низ (дно) - 0000h.

Когда мы вызываем какую-нибудь подпрограмму командой call, процессор кладет в стек адрес следующей за командой call инструкции. Следить за стеком позволяет пара регистров SS:SP. Многие уже, наверное, заметили, что при запуске какой-нибудь com-программы регистр SP равен 0FFFEh, а сегментный регистр SS, как уже упоминалось в предыдущих выпусках, равен нашему единственному сегменту (CSEG) (как и CS, DS, ES).

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

Напечатайте такую программу в редакторе (обязательно!):

CSEG segment

assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG

org 100h

begin:

call Our_proc

int 20h

Our_proc proc

ret

Our_proc endp

CSEG ends

end Begin

Ничего сложного... Запускаем отладчик.

Итак, смотрим на пару регистров SS:SP. SS=CS=DS=ES (это понятно). SP=0FFFEh (т.е. указывает на вершину стека).

Теперь заходим в процедуру. Для CV нажимаем F8, для AFD F1.

Опа! SP изменился. Он уменьшился на 2. Компьютер поместил в стек адрес возврата из процедуры (на инструкцию int 20h) (помните прошлые выпуски?). Проще говоря, call перешел на метку Our_proc, поместив в стек адрес возврата из этой подпрограммы.

Нажимаем еще раз F8/F1. Что получилось? SP опять изменился! Но теперь он увеличился на 2 (стал равным 0FFFEh). Т.е. команда ret взяла из стека адрес возврата и перешла по нему. Как раз на int 20h.

В данном случае говорят, что мы выравнили стек. Он был изначально равен 0FFFEh и остался равен перед выходом 0FFFEh.

Вот один из способов использования стека. Но на этом мы не остановимся и рассмотрим несколько операторов, которые позволяют работать со стеком.

Оператор Перевод Применение Процессор
PUSH приемник push - втолкнуть Поместить в стек число 8086
Оператор Перевод Применение Процессор
POP приемник pop - вытолкнуть Достать из стека число 8086

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

Вот как это запишется:

...

(1) mov ax,345h

(2) push ax

(3) mov ah,10h

(4) int 16h

(5) pop ax

...

Здесь мы загружаем в AX число 345h, сохраняем его, ждем нажатия клавиши (при этом сама клавиша будет в AX, т.е. AX изменится) и восстанавливаем AX. В итоге AX будет содержать число 345h, что, как говорится, и требовалось доказать.

Однако, стоит заметить такой момент. Допустим, мы помещаем в стек следующие регистры: AX, BX, CX:

push ax

push bx

push cx

Обратите внимание, что восстанавливать со стека нужно в обратном порядке:

pop cx

pop bx

pop ax

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

mov ax,1234h

mov bx,5678h

push ax

push bx

pop ax

pop bx

В итоге AX будет равен 5678h, а BX - 1234h.

Но в процедурах необходимо очень тщательно следить за стеком. Вот пример:

...

call Our_proc

int 20h

...

Our_proc proc

mov ax,15

push ax

mov ah,9

mov dx,offset Str

int 21h

ret

Our_proc endp

...

Обратите внимание, что мы "забыли" восстановить из стека AX в найшей процедуре (Our_proc). Что произойдет? Компьютер, дойдя до оператора ret, вытащит из стека не адрес возврата, а число 15 и перейдет на этот адрес. Что находится по адресу 15 - не известно. Машина, скорее всего, "зависнет". Надеюсь, что это понятно.

Следить за стеком (как уже говорилось) позволяет пара регистров SS (сегмент):SP (смещение).

Программист может менять как сегмент, так и смещение, но при этом следует иметь ввиду, что перед сменой регистров SS и SP необходимо запретить все прерывания, а после изменения разрешить (запрет прерываний вешает компьютер! Восстановление же - приводит к нормальной работе. После команды cli всегда должна идти sti, иначе компьютер виснет!). Это позволяют сделать следующие операторы:

Оператор Перевод Применение Процессор
CLI CLear Interrupt - запретить Запретить прерывания 8086
Оператор Перевод Применение Процессор
STI reSTore Interrupt - восстановить Разрешить прерывания 8086

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

Когда вы создали прогамму и запустили ее на выполнение, то работает не только она одна. Простейший пример - таймер, который вызывается 18 раз в секунду для обновления. Компьютер всегда что-то делает! Даже тогда, когда ждет от вас нажатия клавиши. Что происходит, когда вызывается прерывание от таймера? Примерно тоже, что и при вызове процедуры. Компьютер запоминает в стеке адрес текущей команды, а также все регистры и переходит на адрес прерывания, по которому находится процедура обработки этого прерывания (например, таймера, которая обновит показания часов/минут/секунд). Затем, как процедура отработала, компьютер восстановит из стека адрес возврата и все регистры, и наша программа пойдет работать дальше.

Вот пример, в котором мы изменим регистры SS:SP, не запрещая прерывания:

(1) mov ax,100h

(2) mov ss,ax

(3) mov sp,200h

Допустим, прерывание таймера сработало после строки (2). Что у нас получилось? SS равен 100h, а SP еще не успел измениться. Получается, что сегмент стека верный, а смещение - осталось прежним (допустим, SP=0FFFEh). В итоге, SS=100h, а SP=0FFFEh. Компьюетр и сохранит по этому адресу данные. Какой при этом код программы затрется - не известно. Мы ведь хотели сделать SS=100h, а SP=200h! Хорошо, если две строки успели выполниться перед вызовом прерывания. Хорошо также, если по адресу 100h:0FFFEh ничего нет (память свободна). А если есть? Тогда компьютер, скорее всего, "зависнет".

Отсюда вытекает два правила:

1) Прежде чем менять регистры SS:SP, необходимо запретить все прерывания командой cli, а затем разрешить командой sti.

2) SS:SP нужно устанавливать на свободную область памяти. При этом следует убедиться, что код не утратил работоспособности.

Мы знаем, что после загрузки com-программы в память, SS равен сегменту, куда загрузилась программа, а SP - 0FFFEh. Код программы начинается с адреса 100h (org 100h). Вершина стека - конец нашего сегмента. Если наша программа занимает, скажем 2000h байт, то можем установить SP в 2200h. В этом случае мы отводим 100h (именно сто) байт для стека (т.к. программа загружается по адресу 100h, то 2000h нужно прибавить 100h). Стек, как вы помните, растет снизу вверх. Если мы переполним стек (например, поместим более 100h данных), то затрется часть нашей программы снизу. Имейте это ввиду!

Друзья мои! Это понятно? oleg77@online.ru?Subject=Ничего не понял в стеке

Вот пример изменеия регистров стека:

cli

mov ax,0B900h

mov ss,ax

mov sp,100h

sti


Программка для практики.

Прежде чем перейти к программе, рассмотрим новый оператор:

Оператор Перевод Применение Процессор
NOP No OPerand - нет операнда Ничего не делает 8086

Этот оператор делает то, что ничего не делает, но занимает один байт. Его обычно используют для резервирования места либо для того, чтобы "забить" ненужный код, когда исходник на Ассемблере отсутствует. Например, программа перед стартом проверяет версяю MS-DOS. Версия, которая установлена на вашем компьютере, не соответствует требуемой программой. Для этого данным оператором "забивают" участок кода, который проверяет версию ОС.

Все это позволяет сделать Hacker's View, который можно взять на моем сайте:

www.oleg77.newmail.ru/Assembler/For_assemblers.html

Запомните машинный код данной комманды: 90h.

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

(1) CSEG segment

(2) assume cs:CSEG, es:CSEG, ds:CSEG, ss:CSEG

(3) org 100h

(4) Begin:

(5) mov sp,offset Lab_1

(6) mov ax,9090h

(7) push ax

(8) int 20h

(9) Lab_1:

(10) mov ah,9

(11) mov dx,offset Mess

(12) int 21h

(13) int 20h

(14) Mess db 'А все-таки она выводится!$'

(15) CSEG ends

(16) end Begin

То, что вы видите - обман зрения. На первый взгляд, программа что-то делает с регистром SP, а затем выходит. Строки (9) - (12) вообще не будут работать. Но это глубокое заблуждение!

Попробуйте запустить ее под отладчиком. Вы увидите, что CodeView, TurboDebuger, AFD будут сообщать какую-то ерунду (непонятные операторы, сообщения типа "программа завершилась", хотя строка не выведена и пр.). Но, если запустить ее просто из ДОС, то строка появится на экране, т.е. программа будет работать корректно!

Данный пример - типичный случай "заламывания рук" отладчикам (но не SoftIce!). И вы уже можете это делать!

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

Ваша задача: разобрать "по полочкам" программу. Почему так происходит? Почему строка выводится? Почему отладчик работает неверно? И пр. и пр. Вопросов море. И вам предстоит дать ответ на них.

Разобравшись с программой самостоятельно, вы почувствуете Силу и Неограниченные Возможности Ассемблера. И это правда! Не спешите писать мне письмо с просьбой помочь. Будет не интересно! Пробуйте разобраться сами! Не бойтесь эксперементировать! Компьютер будет часто зависать, но это не главное! Ваши мучения приведут вас к Истине! Я сам проходил когда-то через это...

Впечатлениями можно делиться со мной:

oleg77@online.ru?Subject=Впечатления

Давайте попробуем встретиться в чате в понедельник (07.08.2000) в 09:30 утра по московскому времени:

http://www.chats.ru/users_chats/oleg77/

Жду вас всех (если удастся пробиться)!

Нескучной Вам недели!

С уважением,

Калашников Олег ( oleg77@online.ru?Subject=Ассемблер: )


Немного рекламы.

MASM можно взять здесь:

http://www.microsoft.com/ddk/download/98/BINS_DDK.EXE (2.9 MB).

http://www.microsoft.com/ddk/download/98/98SETUP.EXE (2.3 MB).

___________

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

С предыдущими выпусками можно ознакомиться здесь:

www.subscribe.ru/archive/law.russia.buyers

Подписаться можно в приведенной ниже форме.

Рассылки Subscribe.Ru
Законодательство РФ: права потребителей

Спасибо всем!



http://subscribe.ru/
E-mail: ask@subscribe.ru

В избранное