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

Ассемблер? Это просто! Учимся программировать Выпуск N 025 (Оболочка)


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Ассемблер? Это просто! Учимся программировать
______________________________________

Выпуск N 25 (Оболочка)

Я пiду до рiченкi стрiчати зiрочки,
Зазiраць як падаюць, ловити iх жменями.
Забiрусь на райдуго, взлечу по-пiд хмарами.
Перадам по радiо: "Прощай рiдна Батьковщино!"
Весна, весна, весна прыйде,
Весна, весна, весна вгамуе...

Здравствуйте, уважаемые подписчики!

Долгожданная весна наступила! УРА!

Сегодня в номере:

  • Информация для новеньких

  • Командировка. Иные вопросы

  • Основы работы с сопроцессором

  • Оболочка. Вывод десятичных чисел. Длинные имена файлов



  • Благодарю Вас, что подписались на рассылку "Ассемблер? Это просто! Учимся программировать". Надеюсь, что Вы не останетесь равнодушными к ней и почерпнете море полезной информации, а также повысите свой уровень в "общении" с IBM-совместимыми компьютерами.

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

    Всю нужную информацию (предыдущие выпуски, адреса экспертов, необходимые программы, документацию, а также многое другое) можно найти на сайте http://www.Kalashnikoff.ru. Рекомендую Вам прежде ознакомиться с разделом "Информация для новых подписчиков".

    Если у Вас нет выхода в Сеть, то предыдущие выпуски рассылки, информацию для новеньких и адреса экспертов можно получить по почте, направив пустое письмо по адресу AssmIssues@Kalashnikoff.ru. Информация (320 Кб) будет выслана Вам в течение двух рабочих дней с момента получения Вашего письма. Однако, пожалуйста, не злоупотребляйте этим, т. к. высылка писем подобного объема несет дополнительную нагрузку на почтовые сервера.



    Командировка. Прочие вопросы

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

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

    Побывал в классном ночном клубе "Сахара". К сожалению, адрес так и не запомнил. Рекомендую всем туристам!

    _________

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

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

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

    _________

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

    _________

    Я теперь разместил на сайт полный дистрибутив MASM 6.12. Если вы еще не запаслись им, то - качайте! На сайте теперь можно также найти TurboDebugger 5.0, Multi-Editor 8.0 и многое другое. Пользуйтесь!!!

    _________

    В предыдущих выпусках я писал про уникальный сайт Эдуарда Дмитриева "Библиотека программиста" (http://prog.agava.ru), где можно найти много полезной информации по программированию на различных языках. К сожалению, я поспешил немного. Т.к. у AGAVA были проблемы, то вся информация потерялась. В результате, многие, обратившиеся на данный сайт не нашли там того, что искали. Теперь ситуация более-менее исправлена. Заходите! Это действительно неплохая вещь! А я пока с Эдуардом подумаю по поводу объединения наших рассылок...



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

    FAQ по сопроцессору, придуманный мной лично.

    Что такое сопроцессор?
    Сопроцессор (FPU - Floating Point Unit) - это специальное устройство, устанавливающееся либо на материнскую плату, либо встраиваемое внутрь основного процессора (расположенное на одном кристалле с ним).

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

    Есть ли в моем компьютере сопроцессор?
    В старых машинах сопроцессор устанавливался на материнскую плату как отдельное устройство. Начиная с 80486DX сопроцессор встраивается внутрь основного процессора. Смею вас уверить, что если на вашем столе стоит 80486DX-40 или (на худой конец!) Pentium, то сопроцессор в нем присутствует.

    Наличие сопроцессора в машине с некоторой уверенностью можно было определить из названия (типа) (386SX - сопроцессор отсутствует, 386DX - сопроцессор присутствует. Аналогичная ситуация и с "четверками"). Хотя были "умельцы", которые умудрялись выпаивать (!) сопроцессоры из машин класса DX! Наверное, ценились они раньше...

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

    А как вообще узнать, использует ли сопроцессор та или иная программа в процессе работы?
    В принципе, большинству программ не требуется помощь сопроцессора. Они просто не производят сложные математические расчеты. Однако я встречал даже антивирусы, которые требовали наличие FPU! Что там чрезвычайно сложное считали они - не совсем понятно...

    Программы можно разделить на следующие типы:

    1. Не использующие сопроцессор;

    2. Использующие сопроцессор. При его отсутствии выводят соответствующее сообщение на экран и завершаются;

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

    Определить, использует ли та или иная программа команды FPU возможно только одним способом: дизассемблировать ее и поискать команды сопроцессора (либо сделать это в процессе отладки).

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

    Сложно ли программировать сопроцессор на Ассемблере?
    Да, в общем-то, нет. Главное - навык...

    P.S. Вдумайтесь в фразу: "Да, в общем-то, нет". Все-таки русский язык самый гибкий...

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

    Во-вторых, то, что мы делаем с помощью сопроцессора, сложно реализовать без него.

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

    "А что мы делаем с сопроцессором?"
    Мы будем выводить целые десятичные числа на экран. Например, размеры файлов. Это, скажу вам, гораздо проще, чем использовать только процессор.

    "А что, в Ассемблере очень сложно вывести десятичное число на экран? В Бейсике, например, достаточно набрать PRINT 25*4 и мы увидим результат умножения..."
    На Ассемблере очень просто вывести на экран шестнадцатеричные и двоичные числа, но никак не десятичные. Поэтому только в 25 выпуске мы затрагиваем данную тему, так как вы должны уже неплохо разбираться в командах Ассемблера и понимать принцип программирования на этом мощном и очень простом языке.

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

    "А если я ничего не пойму?"
    Не отчаивайтесь! Все станет понятно со временем. Вспомните, у вас, вероятно, были трудности с тем или иным оператором, процедурой. Но в процессе экспериментов все стало на свои места. Так и сдесь. Если вы сразу поймете все, что написано в данном выпуске, то, можно сказать, что я добился того, чего хотел. Если нет - экспериментируйте, спрашивайте. Эту тему желательно усвоить ВСЕМ!

    ___________

    Итак, приступим.

    Прежде всего стоит отметить, что для того, чтобы использовать инструкции сопроцессора, необходимо включить в начале программы директиву .8087 (.287, .387). Т.е. указать, команды какого сопроцессора мы будем использовать. Принцип такой же, как и при указании процессора: либо только 8086 (.8086), либо 8086 и 80286 (.286) и т.д. Вот пример включения директивы, которая указывает программе-ассемблеру инструкции какого сопроцессора будут использоваться:

    .386 - не забывайте указывать также процессор!
    .287 - будем использовать команды не только 386 процессора, но и 8087, а также 80287 сопроцессоров.

    В некоторых случаях TASM может выдавать сообщение при попытке ассемблирования программы, начинающейся с указанных выше строк. Что-то типа: "Внимание: использование команд FPU (.287) не равны командам центрального процессора (.386)!" Зачем нужно было это делать - ума не приложу! Программист может использовать, например, команды 486 процессора, а также 8087 сопроцессора. Ничего страшного в этом нет и быть не может! В любом случае просто игнорируйте это сообщение.

    _________

    Все, что имеет сопроцессор, - это набор нескольких инструкций и 8 регистров (я бы даже сказал, 8 ячеек памяти). Принцип загрузки числа в тот или иной регистр похож на принцип работы стека. Как так? Да очень просто! Ниже будем много тренироваться. Пока только теория.

    Регистры имеют следующие названия: ST(0), ST(1), ST(2) ... ST(7). Иногда для краткости ST(0) называют просто ST. Загрузить в любой из указанных регистров число командой MOV не получится. Для этого существуют специальные команды сопроцессора.

    Хорошим тоном перед использованием сопроцессора является его инициализация командой FINIT. Вообще, все команды сопроцессора начинаются с символа F, что является его отличительной чертой.

    Инструкция FINIT инициализирует сопроцессор. При этом сбрасываются все управляющие флаги (биты, регистры), а также очищаются 8 регистров ST. Т.о. программист перед работой может быть уверен в том, что никакой регистр не занят и не получится "каши" или переполнения стека (регистров) при попытке выполнить ту или иную операцию.

    Сопроцессор может работать с 16 - 80 разрядными числами. Следовательно, в регистры ST(0) - ST(7) можно загружать число в указанных выше пределах. Сопроцессор сам определит, 16-разрядное ли это число или 64-х рязрядное.

    Все числа загружаются в регистры сопроцессора и выгружаются из них в/из обычных переменных. Регистр ST(0) является, скажем так, главным. Только в него можно загружать числа из переменных и выгружать в переменные.

    Например. Возьмем команду FILD, которая загружает в регистр ST(0) число из переменной в памяти:

    FILD Number1

    ...

    Number1 dw 10

    Как видите, здесь мы указываем только имя переменной, значение которой будет загружено в регистр ST(0) и только в него (как уже упоминалось).

    Допустим, нам нужно сложить два целых числа, используя сопроцессор. Для этого мы загружаем в ST(0) число 10. Затем загружаем второе число (для сложения ведь нужно как минимум два числа):

    (1) FILD Number1
    (2) FILD Number2

    ...

    Number1 dw 10
    Number2 dw 3

    Теперь внимание! После выполнения строки (1) ST(0) будет содержать число 10. После выполнения строки (2) число из ST(0) переместиться в регистр ST(1), а его место в ST(0) займет число 3. Т.е. система похожа на работу стека! Далее нужно сложить числа, которые находятся в ST и ST(1). Вот так:

    FADD

    Команда FADD без параметров складывает два числа, которые находятся в регистрах ST(0) и ST(1), при этом очищая ST(1) и помещая результат сложения в ST(0).

    Рассмотрим, что происходит в регистрах в процессе выполнения данных команд. Итак, пока никаких действий мы не выполняли:

    ST(0) Пусто
    ST(1) Пусто
    ST(2) Пусто
    ... ...
    ST(7) Пусто

    Заносим первое число в регистр ST командой FILD Number1:

    ST(0) 10
    ST(1) Пусто
    ST(2) Пусто
    ... ...
    ST(7) Пусто

    Теперь второе число командой FILD Number2:

    ST(0) 3
    ST(1) 10
    ST(2) Пусто
    ... ...
    ST(7) Пусто

    Обратите внимание, что числа как бы вталкиваются внутрь. Как уже отмечалось, заносить числа в сопроцессор можно только в регистр ST(0). Поэтому-то второй параметр в команде FILD отсутствует: и так все понятно...

    Теперь складываем оба числа командой FADD:

    ST(0) 13
    ST(1) Пусто
    ST(2) Пусто
    ... ...
    ST(7) Пусто

    Вот, что получилось!

    Как нам теперь получить результат?

    Существует также команда, противоположная FILD:

    FIST Result

    Эта команда сохранит число, которое находится в регистре ST(0), в переменную Result.

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

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

    WAIT
    ESC ...

    Однако, лучше всего отлаживать программу, которая использует инструкции сопроцессора в отладчике TurboDebugger. Если у вас нет этого отладчика, то его можно найти на сайте (версия 5.0). Я настоятельно рекомендую отлаживать программы и смотреть результаты выполнения именно в этом отладчике, т.к. он очень удобен и наглядно отображает все, что вам нужно. Более того, я специально настроил рабочий стол TD5.0 так, чтобы вы видели все необходимое (регистры сопроцессора, результаты вычислений, код программы, дамп памяти).

    Если вы посмотрели работу приведенной выше команды в TD, то читаем дальше...

    Вопрос: вот мы сложили два числа, получили результат, но ведь в ST(0) осталось число 13! Получается, что нам нужно перед каждой арифметической операцией инициализировать сопроцессор командой FINIT чтобы очистить регистры от результатов вычислений? Можно ли как-то убрать это число иным способом. Например, за один раз (одной командой) перенести его в переменную Result и освободить ST(0)?

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

    Как правило, мнемоника команд сопроцессора имеет определенную систему, подобную мнемоникам команд процессора, а имеено: команда представляет из себя сокращения английских слов (например, XCHG - eXCHanGe - обменять). Вот пример:

    • FILD - Integer LoaD - загрузка целого числа (в регистр сопроцессора)

    • FADD - ADDition - сложение

    • FIST - Integer STore - сохранение целого числа (в переменную в памяти)

    • FISTP - Integer STore and Pop - сохранение целого числа и выталкивание его из ST(0)

    Если мы заменим

    FIST Result

    на

    FISTP Result

    то в ST(0) ничего не останется. Думаю, что это достаточно удобно. Советую поэкспериментировать пока...

    ________

    Прежде, чем приступим к практике, хочу сказать еще несколько слов о работе сопроцессора.

    Сопроцессор имеет специальный регистр управления. Он необходим, в частности, для указания сопроцессору метода округления действительного (вещественного) числа (числа с точкой или еще говорят: "числа с плавающей точкой"). Например, число 23,8 может быть округлено следующим образом (в зависимости от установленных битов в регистре управления): до 24, либо до 23, либо без округления вообще. Или, например, если установлен тот или иной бит определенного регистра, то сопроцессор производит вычисления с более высокой точностью или с более низкой. Как в некоторых калькуляторах стоит переключатель разрядности (количеста знаков после запятой).

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

    ________

    Теперь попробуем разобрать программу, которая выводит на экран целое десятичное число. Никаких объяснений я не даю. Нет смысла. Ниже приведена программа, которая выводит в центре нулевой строки заданное число в десятичной форме. Все очень просто! Самое главное, что нужно сделать - посмотреть эту программу под отладчиком TD да так, чтобы были видны регистры сопроцессора при отладке.

    Ну-с, друзья мои, приступим!!!

    Как вывести десятичное число на экран в Ассемблере?

    Возьмем 1234. Естественно, в десятичном формате. Т.е. выводить будем число одну тысячу двести тридцать четыре.

    На первый взгляд кажется, что достаточно отделить четверку и вывести ее на экран, затем 3, дальше - 2 и т.д. Например, используя известную нам команду AND:

    mov AX,1234
    and AX,0004 или and ax,0Fh

    Либо как-то иным способом. Но проблема в том, что число 1234 будет представлено в шестнадцатеричном формате или (что вернее) в двоичном. Вот, что мы увидим в отладчике:

    mov ax,4D2h
    and AX,4

    И что же мы получим в AX? В первом случае (and AX,0004) мы получим нуль, а во втором (and ax,0Fh) - 2. Почему? Подумайте хорошо! Ведь мы уже проходили двоичную и шестнадцатеричную системы счисления, а также логические команды. Прежде, чем читать дальше, вы должны понять почему так происходит...

    В любом случае подобные способы не годятся.

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

    1234/10=123 остаток 4
    123/10=12 остаток 3
    12/10=1 остаток 2
    1/10=0 остаток 1

    Что получили: 4321. Т.е. 1234 в обратном порядке. Следовательно, и выводить на экран будем "задом наперед".

    Можно, конечно, разделить на 1000, затем на 100, 10... Но ведь числа бывают разные! Длинные, короткие, средние...

    Проще всего, как уже отмечалось, делить на десять до тех пор, пока делимое (т.е. выводимое число, т.е. в нашем примере - 1234) не будет равно нулю.

    "Хорошо, тогда как нам разделить его на 10, записать остаток, а затем вывести его на экран?" Вот это мы и будем делать с помощью сопроцессора. Подобный алгоритм будет использоваться в нашей оболочке (пока!). Я специально вынес его в отдельный файл, чтобы вам было удобнее разобраться, что происходит. Сразу отмечу, что более "корявых" алгоримов уже не придумаешь. Я имею в виду, что наш алгоритм будет слишком сложный и замудреный. Зачем я сделал так? Хоть этот алгоритм и далек от совершенства, но зато он даст вам вомозножность понять принцип работы сопроцессора. Для этого (еще раз подчеркну) следует разобрать приведенную ниже программу в отладчике TurboDebugger или подобном ему.

    Видите, сколько разных программ нужно иметь под рукой для того, чтобы писать на Ассемблере. И это еще не предел. Скоро будем работать с SoftIce'ом, дизассемблером и пр. Так что, готовьтесь и привыкайте потихноньку... Зато ваши знания будут несравнимы со знаниями программистов, работающих с языками высокого уровня. После того, как вы изучите Ассемблер (если хватит терпения), вы сможете без особого труда разобраться с любой программой, написанной на языке высокого уровня. В любом случае, можно будет ее скомпилировать, а затем дизассемблировать и посмотреть код. Но это так, "лирическое отступление"...

    Файл !coproc!.asm

    Читайте описания, ассемблируйте, запускайте под отладчиком и... познавайте!




    Sshell25.asm (головной файл)

    Display.asm

    Files.asm

    Keyboard.asm

    Main.asm


    Messages.asm

    Data.asm

    Файл-приложение в Интернете (включая "!coproc!.asm"): http://www.Kalashnikoff.ru/Assembler/Issues/Enclosures/Sshell25.rar.

    Ну а что тут? Да, в принципе, все просто. Как всегда. Теперь мы выводим длинные имена файлов, а также их размер.

    Как получить и вывести длинное имя файла? Для этого следует воспользоваться НЕ функциями 4Eh и 4Fh, а 714Eh и 714Fh прерывания 21h. Вот так:

    mov ax,714Eh ;Функция поиска первого файла
    xor di,di ;DI должен указывать на буфер, куда будут записываться данные о найденном файле (типа DTA).
    xor si,si ;SI пока остается тайной!
    mov cx,0FFh ;Ищем все возможные файлы. Это что-то вроде атрибутов файла
    mov dx,offset All_files ;Маска поиска (*.*)
    int 21h ;Теперь в ES:DI находится информация о найденном файле!
    mov Handle,ax ;Номер процесса поиска файлов

    Отличие данной функции еще в том, что она требует указания номера, который мы сохраняем после вызова 714Eh.

    Все остальное подробно описано в файле-приложении (см. FILES.ASM).

    Процедура вывода десятичных чисел находится в файле DISPLAY.ASM. Я очень надеюсь, что труда разобраться с программой не составит.

    Как обычно все файлы-приложения содержат большое количество комментариев. Пользуйтесь отладчиком!

    Ваши варианты вывода десятичных чисел на экран можно присылать по адресу: Assembler@Kalashnikoff.ru. В последующих выпусках они будут опубликованы в обязательном порядке.

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

    До встречи через неделю!!!


    С уважением,

    Калашников Олег: Assembler@Kalashnikoff.ru
    ICQ No.: 68951340
    URL сайта подписчиков: http://www.Kalashnikoff.ru

    ______________

    По вопросам сотрудничества, рекламы и спонсорства обращайтесь:


    (C) Москва, 2001. Авторское право принадлежит Калашникову О.А. Публичное размещение материала из рассылки, а также его использование полностью или частично в коммерческих или иных подобных целях без письменного согласия автора влечет ответственность за нарушение авторских прав.


    http://subscribe.ru/
    E-mail: ask@subscribe.ru
    Поиск

    В избранное