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

Программирование на ассемблере под Windows с нуля


Информационный Канал Subscribe.Ru


В поисках истины

или программирование на ассемблере под Windows с нуля.


Выпуск №4

Сегодня в выпуске:


Обращение к подписчикам

    Для вас у меня сегодня две новости ;-), одна хорошая другая ... ещё лучше =).
   1) В следущем выпуске вы напишите свою первую программу !!! Поэтому вам необходимо будет запастись двумя инструментами: ассемблером и отладчиком, от том какие инструменты нам нужны, я более чем подробно рассказал в прошлом выпуске.
   2) Вы можете влиять на содержание рассылки, то есть, я буду отбирать наиболее интересные вопросы и по возможности буду отвечать на них в рамках рассылки. Только учтите, если я говорю, что вам не надо забивать себе головы какими-то новыми понятиями, значит так оно и есть, но если вопросы возникают по обсуждаемым темам, я с удовольствием на них отвечу.



Глава №2

Программная модель процессора.

(Первое погружение в недра процессора).

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

   У каждого регистра есть свое имя, оно нужно для того чтобы процессор точно знал, к какому регистру мы обращаемся, и с содержимым какого регистра мы будем работать. Вот имена тех регистров, с которыми вы познакомитесь на этом занятии - eax, ebx, ecx, edx. Важнейшим вопросом при рассмотрении регистров является их размерность, то есть, сколько мы можем "разместить" информации в этих "ящиках". Скажу сразу, что эти четыре регистра имеют размерность 32 бита, т.е. double word. Вы помните, как это будет по-русски ;-) ? "Заглянув" в этот "ящик" мы сможем убедиться, что он разделен на "секции", а каждая "секция" разделена еще на маленькие "отсеки". Итак, рисуем наш "ящик-регистр" таким, каким он представляется программисту.

EAX
   ax
   ah al
1 0 1 1 0 1 0 1 1 1 1 1 0 0 1 0 1 1 0 1 0 1 1 1 0 0 0 1 0 1 0 1

   Да, уж ... Ну и как это понимать? Давайте внимательно посмотрим на этот рисунок. Первое что бросается в глаза это то, что большой регистр eax разбит на две равные части размером в одно слово. Одно слово имеет свое имя - ax, другое - нет. В свою очередь ax разбит еще на две части, замечу что регистр разбит на части только логически, физически он представляет собой единое целое. Эти две равные части, на которые разбит ax, имеют свои названия - ah, al. Если не уходить в дебри, то можно сказать, что это разделение сохранено, для того чтобы старые программы, написанные для 16-битных процессоров, могли работать на современных компьютерах. Программы, написанные для "древних" процессоров, знать не знают о таком регистре как eax, а вот регистр ax им хорошо знаком. Продолжаем разбирать нашу схемку. Самое время попытаться расшифровать "аббревиатуру" eax. E - extended (расширенный), A - accumulator (аккумулятор). Расширенным этот регистр является по отношению к базовому 16-рязрядному регистру ax, регистр eax, как я уже сказал, ровно в два раза больше чем ax. В большинстве случаев регистр eax, и собратья ebx,ecx и edx, могут быть использованы на наше усмотрение. Но в определенных ситуациях, их использование предопределено. Например, при организации цикла регистр ecx исполняет роль счетчика. Внутреннее устройство регистров ebx, ecx и edx аналогично устройству регистра eax. Схематично устройство регистров можно изобразить таким образом:

eax
  ax
  ah al

ebx
  bx
  bh bl

ecx
  cx
  ch cl

edx
  dx
  dh dl

    Теперь расшифруем остальные три "аббревиатуры" - ebx, ecx, edx:
EBX (Base register) - базовый регистр. Применяется для хранения базового адреса некоторого объекта в памяти.
ECX (Count register) - регистр-счетчик. Как я указал выше, он применяется в качестве счетчика в при организациициклов.
EDX (Data register) - регистр данных. Как и регистр eax, является хранилищем промежуточных данных. В некоторых командах его использование обязательно.
    Хочу повторить еще раз, что только в некоторых ситуациях регистры являются такими "узкоспециализироваными",в большинстве же случаев вы можете использовать их на свое усмотрение.

    Разделение регистров на "части" имеет массу удобств. Мы можем обратиться к любой именованной части регистра. С ними мы можем работать как с полноценными регистрами. А вот к старшей части регистров eax, ebx, ecx, edx напрямую мы обратиться не можем.

   Для закрепления полученных знаний по регистрам, напоминаю размерность регистров, eax - 32 бита (двойное слово), ax - 16 бит (слово), ah и al - по одному байту. Ну и что бы уж совсем стало ясно, еще раз то же самое только немного другими словами, вернее числами =), eax - FFFFFFFF, ax - FFFF, ah и al - по FF. Если кто то сомневается, что это числа, прошу еще раз прочесть прошлые выпуски ;-).

    Что же мы можем делать с этими регистрами, вернее не с ними, а с теми данными, которые в них находятся. Так как в регистрах у нас находятся числа, то, естественно предположить, что мы можем проводить любые арифметические действия над этими числами. Самая первая арифметическая операция, с которой я вас познакомлю, будет сложение. Но прежде чем что-то с чем-то складывать необходимо, что-то куда-то положить ;-). Естественно что, размещать наши числа будем в регистры, для этого они и предназначены =). Итак, первая команда, с которой вы сегодня познакомитесь, помещает в регистр какое-либо число. Имя этой низкоуровневой команды - MOV. Легко догадаться, что имя этой команды произошло он английского слова "move", в нашем случае оно переводится как "поместить". Как же мы можем использовать данную команду? Мы можем поместить абсолютное значение, т.е. число.

              mov EAX,089ABCDEFh ; помещаем число 89ABCDEF в регистр eax
    mov AX,0ABCDh ; помещаем число ABCD в регистр ax
mov ah,0ABh ; помещаем число AB в регистр ah

    С помощью этого символа ";" отделяются комментарии от ассемблерных команд. Как видно из этих трех строк, мы можем с помощью команды "mov" обращаться и ко всему регистру eax, и к его логическим частям. Так же мы можем поместить значение одного регистра в другой регистр. Тут главное запомнить, если вы собираетесь поместить в регистр значение другого регистра, то необходимо соблюдать размерность. Поясняю, не стоит в 16-битный регистр ax запихивать значение из 32 битного регистра ebx, это закончится очень плохо :-(.

     mov EAX, EBX ; помещаем значение регистра ebx в регистр eax
mov AX, CX ;помещаем значение регистра cx в регистр ax
          mov ah, dl ; помещаем значение младшего байта dl в старший байт ah

   Так же с помощью команды mov в регистр можно разместить число из памяти, проще говоря переменную, или константу. И еще кое-что, но об этом в другой раз на сегодня об этой команде достаточно, переварите хотя бы это =).

   Если вы и сейчас не поняли, что же делает команда mov, даю последнюю подсказку. Вот её "Паскальный" аналог

    AX:= 0abcdh
AX:= BX

   Ну, уж если и теперь не поняли, тогда я умываю руки ;-).

    Как и было заявлено, рассмотрим сложение. Сложение осуществляется посредством ассемблерной команды ADD - Addition (сложение)

add EAX, 506d ; Сложить значение регистра eax и 506
               add EAX, ECX ; Сложить значение регистра eax и значение регистра ecx

   Теперь, объединив полученные знания по ассемблерным командам, мы можем написать такие "строки"

    mov ax, 05555h
    mov bx, 06556h
add ax, bx

   Итак, разбираемся что мы написали. Сначала присвоили регистру ax, значение 5555h, затем регистру bx - 6556h. После сложили значения этих двух регистров и получили ... Давайте оформим наши рассуждения в виде такой таблички

Команда ax bx
       mov ax,5555h 5555 0000
       mov bx,6556h 5555 6556
add ax,bc baab 6556

По-моему, все более чем понятно.

   Теперь я дам небольшую справочку по этим командам, более точную, но и "сухую", которую я никак не буду комментировать. На то она справка ;-). Из таких справочек, вы сможете в последствии сделать небольшой, или даже большой, как получится, справочник. В таких справках вам будут встречаться неизвестные понятия и термины, не пугайтесь. Все будет подробно объяснено в свое время. Я бы порекомендовал вам их сейчас не читать, все что надо знать об этих командах на данный момент, я уже объяснил. Заведите себе какой-нибудь *.doc, и методом copy-past заполняйте его этими справками.

ADD Целочисленное сложение
Команда add осуществляет сложение первого и второго операндов. Исходное значение первого операнда (приемника) теряется, замещаясь результатом сложения. Второй операнд не изменяется. В качестве первого операнда команды add можно указывать регистр (кроме сегментного) или ячейку памяти, в качестве второго - регистр (кроме сегментного), ячейку памяти или непосредственное значение, однако не допускается определять оба операнда одновременно как ячейки памяти. Операнды могут быть байтами или словами и представлять числа со знаком или без знака. Команду add можно использовать для сложения как обычных целых чисел, так и двоично-десятичных (с использованием регистра АХ для хранения результата). Если складываются неупакованные двоично-десятичные (BCD) числа, после команды add следует использовать команду ааа; если складываются упакованные числа, то команду daa. Команда воздействует на флаги OF, SF, ZF, AF, PF и CF.

MOV Пересылка данных
Команда mov замещает первый операнд (приемник) вторым (источником). При этом исходное значение первого операнда теряется. Второй операнд не изменяется. В зависимости от описания операндов, пересылается слово или байт. Если операнды описаны по-разному или режим адресации не позволяет однозначно определить размер операнда, для уточнения размера передаваемых данных в команду следует включить один из атрибутных операторов byte ptr или word ptr. Команда не воздействует на флаги процессора. В зависимости от используемых режимов адресации, команда mov может осуществлять пересылки следующих видов:
        - из регистра общего назначения в регистр общего назначения;
        - из регистра общего назначения в ячейку памяти;
        - из регистра общего назначения в сегментные регистры DS, ES и SS;
        - из ячейки памяти в регистр общего назначения;
        - из ячейки памяти в сегментный регистр;
        - из сегментного регистра в регистр общего назначения;
        - из сегментного регистра в ячейку памяти;
        - непосредственный операнд в регистр общего назначения;
        - непосредственный операнд в ячейку памяти.
   Запрещены пересылки из ячейки памяти в ячейку памяти (для этого предусмотрена команда movs), а также загрузка сегментного регистра непосредственным значением, которое, таким образом, приходится загружать через регистр общего назначения.Нельзя также непосредственно переслать содержимое одного сегментного регистра в другой. Такого рода операции удобно выполнять с использованием стека.



Отвечая на вопрос подписчика

   Вопрос Я вот хотел бы узнать, а как пишутся не целые числа (12345,6789)
Ответ Дробные числа в любой системе счисления записываются одинаково:

Двоичное дробное число 10101,1101b

Шестнадцатеричное дробное число abcd,ef

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

bin to dec

Это один из самых простых переводов, осуществляется по "страшной" формуле из первого выпуска. Возмем число 10101110,1011b, и переведем его в десятичную с.с.

10101110,1011 = 1 * 27 + 0 * 26 + 1 * 25 + 0 * 24 + 1 * 23 + 1 * 22 + 1 * 21 + 0 * 20 + 1 * 2-1 + 0 * 2-2 +1 * 2-3 + 1 * 2-4

Положительные степени двойки были рассмотрены в … выпуске. Вот таблица отрицательных степеней двойки.

Отр. степени число
-1 0,5
-2 0,25
-3 0,125
-4 0,0625
-5 0,03125
-6 0,015625
-7 0,0078125

Вы уже сами можете посчитать, сколько это будет. Главное запомнить, что первая цифра после запятой имеет - 1 разряд.

Hex to dec


Все делается также как и в случае "bin to dec". Вот табличка отрицательных степеней по основанию числа 16.

Отр. степени число
-1 0,0625
-2 0,00390625
-3 0,000244140625
-4 0,0000152587890625
-5 0,00000095367431640625
-6 0,000000059604644775390625
-7 0,0000000037252902984619140625

   Фу, устал набивать =) .

Hex to bin, bin to hex

   Это самое простое =) . Как и в случае с целыми числами, мы заменяем hex-цифру на тетраду bin-цифр. Примерчик:

9    6   5,     a   b   c
1001 0110 0101, 1010 1011 1100

    Последние нули выделены по особому, так как они лишние. И в итоге мы получим такое число 100101100101,1010101111. А произвести обратный перевод, не составит труда.

Теперь самое сложное.

Dec to bin

   Возьмем, для простоты вычислений, такое число - 123,45. Первое что мы делаем, это отделяем целую часть числа от дробной. Целую часть мы переводим согласно хорошо известному вам алгоритму. Дробную часть, в отличие от целой, мы будем не делить на 2, а умножать. Итак, выделяем дробную часть (0,45), умножаем, как я уже сказал, на 2, полученное число условно разделяем на две части, целую и дробную. Целую часть (я её выделил так [  ]) принимаем в качестве первого после запятой разряда. Мы продолжаем умножение до тех пора пока дробная часть, полученная после умножения, не станет равна нулю(подумайте в каком случае это произойдет ;-) ), либо достигнута необходимая точность вычисления. необходимая точность вычисления.

           0,45
х
                 2
---------
           [0], 9
х
                 2
--------
           [1],8
х
                 2
--------
           [1],6
x
                 2
--------
           [1],2
x
                 2
-------
           [0],4
...

   В итоге мы имеем 0,0111. Если вам такая точность недостаточна, считайте дальше сами ;-). Теперь объединим целую и дробную части - 1111011,0111.

Последний рубеж =).

Dec to hex

При переводе дробного числа из десятичной с.с. в шестнадцатеричную сначала переводим число в двоичную систему. Затем двоичное представление разбиваем не тетрады отдельно до запятой и после запятой. Чтобы особенно не напрягаться возьмем наше число 123,45 и переведем его в hex.

123,45 = 111 1011, 0111 = 7b,7
  0111 1011, 0111
  7        b,       7

   При переводе есть небольшая тонкость, если у вас неполная тетрада до запятой, то вы ее дополняете нуля слева, если же неполная тетрада после запятой, тогда нули дописываются справа. Например, 0,1b = 0,1000b = 0,8h.

   На сегодня все.



Обратная связь

   Если вы хотите что-то спросить по ассемблеру, крэку, или просто поболтать прошу на форум нашей команды, там вы сможете получить ответы от меня, и моих товарищей Mafia32, formatC Вы можете отправить письмо на мой почтовый ящик , только в том случае если вопрос имеет отношение к рассылке. Обязательно заполняйте поле "Тема", письма без темы, я не буду читать. Для вашего удобства я разместил в рассылке e-mail форму, вы можете прямо из нее отправлять свое письмо, но для этого должна быть настроена ваша почтовая программа.

Имя:
Тема:
Сообщение:



Копирайты

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

Copyright © 2004 SeDoYHg

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу


В избранное