repead.ru 1


Калашникову

Александру Олеговичу,

сыну моему,

посвящается…


Ассемблер? Это просто!

Учимся программировать под MS-DOS

(По материалам одноименной рассылки с изменениями и дополнениями)



Том I

Практические основы программирования


Часть I

"Знакомьтесь: Ассемблер"

(Редакция 1.20 от 06 декабря 2010 года)


Автор:

Калашников Олег Александрович

http://www.Kalashnikoff.ru


Онлайн-поддержка книги: http://RFpro.ru


Россия, Москва, 1999-2010 года.

Предисловие



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

Основой для книги послужили материалы разработанной автором рассылки "Ассемблер? Это просто! Учимся программировать". Используя данную рассылку, более 18 000 подписчиков научились писать такие программы на ассемблере, которые казались им раньше чрезвычайно сложными и недоступными для понимания или написания. Большая часть подписчиков пыталась раньше изучать язык ассемблера, но так и не смогла пройти полный курс (прочитать ту или иную книгу до конца). Материал рассылки помог им понять ассемблер и научил писать довольно-таки сложные программы под операционными системами MS-DOS и Windows.



Во втором издании автор учёл и исправил все недоработки и ошибки, допущенные в первом, а также по просьбам тысяч читателей добавил в книгу вторую часть, в которой рассматриваются основы программирования на ассемблере под Windows. Всё это поможет вам изучить базовые основы ассемблера за короткое время. Более того, автор попытался сделать обучение как можно более интересным для вас, перейдя с первой же главы к практической части.


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


Уникальность настоящей книги заключается в следующем:


  • в первой части книги рассматриваются базовые операторы ассемблера, основы программирования в реальном режиме (консольные приложения), во второй - основы программирования на ассемблере под Windows;

  • каждая глава соответствует одному занятию, в конце главы приводится файл для практического изучения;

  • материал изложен на простом языке, все новые термины подробно объясняются;

  • исследуется работа отладчиков и способы обойти отладку программы;

  • в процессе изучения ассемблера, начиная с главы 11, рассматриваются четыре программы:

  • безобидный нерезидентный вирус;

  • резидентный антивирус против написанного нами вируса;

  • файловая оболочка (типа Norton Commander®, FAR Manager® и т. п.) с поддержкой длинных имен файлов и использованием XMS-памяти;

  • несколько видов резидентных программ (программ, которые постоянно находятся в памяти);


В целом же разделы с I по III посвящены изучению базовых знаний языка ассемблера с нуля; разделы с IV по VIII – основам программирования на ассемблере под Windows.


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




Несколько советов





  • Обязательно скачайте файлы-приложения для практического изучения курса, а также необходимое ПО с сайта http://www.Kalashnikoff.ru (если таковых нет). Без практики и вспомогательных программ данная книга вряд ли поможет вам научиться программировать на ассемблере.

  • Чаще пользуйтесь отладчиком.

  • Изменяйте код программ (файлов-приложений), больше экспериментируйте.

  • Пробуйте написать свою собственную программу на основе изученного материала.

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

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


  • Все вопросы, которые у вас, несомненно, возникнут в процессе изучения ассемблера, вы можете задавать в любое время нашим экспертам на портале профессионалов http://RFpro.ru. Этот сайт был специально разработан автором книги с целью оказания посетителям помощи по разным направлениям, в том числе и по ассемблеру.




Ответы на некоторые вопросы

1. Почему важно изучить работу процессора в реальном режиме (в MS-DOS) и только после этого переходить к программированию в защищённом режиме (в Windows)?

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

  • Во-вторых, функции WinAPI, о которых пойдёт речь в данной книге, – это прототип прерываний MS-DOS. Но понять принцип работы WinAPI в разы проще на примерах прерываний, которые до сих пор поддерживаются Windows. Как только вы поймёте данный принцип, вы без труда перейдёте на использование WinAPI.

  • В-третьих, учиться основам языка, создавая компактные com-файлые, которые использовались в MS-DOS и до сих пор поддерживаются Windows, гораздо проще.
2. Под управлением каких операционных систем будут работать файлы-приложения?

Компания Microsoft придерживается политики поддержки работоспособности программ, написанных в более ранних версиях собственных операционных систем. Если программа разработана, например, для MS-DOS 3.30, то она будет выполняться и в более поздних версиях этой системы, если, конечно, в самой программе не установлены ограничения, или она не привязана в работе именно к той версии, для которой написана.

Появление на рынке ОС Windows 95/98/ME продолжило эту традицию, оставив возможность загружать и выполнять программы, написанные под операционную систему MS-DOS, и даже запуск DOS-приложений, требующих загруженной "чистой" дисковой системы, к которым относятся, как правило, сложные графические игры, работающие с расширителями, например, DOS4GW.


Тем не менее, большинство программ прекрасно запускаются напрямую из Проводника (Windows Explorer), не требуя перезагрузки системы. Например, Norton Commander, Far Manager, а также необходимые для изучения настоящего курса средства разработки и отладки, перечислен­ные ниже.

В современных операционных системах компании Microsoft (Windows) есть возможность работать в эмуляторе MS-DOS: Пуск -> Выполнить -> cmd -> <Enter>. В открывшимся окне и можно запускать все файлы из данной книги, не обращая внимания на то, что наши COM-программы используют прерывания MS-DOS.


Все примеры протестированы на работоспособность под управлением следующих операционных систем компании Microsoft на IBM-совместимых компьютерах:


  • Windows 2000 Pro и Server;

  • Windows XP Home Edition и Pro;

  • Windows Vista / 2008 / 7.


На ассемблере мы будем напрямую взаимодействовать с аппаратурой, что не очень приветствуется Windows. Поэтому некоторые программы из учебного курса могут работать некорректно. Что делать в таких случаях? Всё просто: отладчик вам поможет!

3. Какое программное обеспечение нужно для того, чтобы создать программу на ассемблере, и где его можно достать?

  • Прежде всего — это текстовый редактор, как отдельный, например, Akelpad, так и встроенный в какую-нибудь оболочку (например, Far Manager). В принципе, сгодится даже обычный Блокнот, т.к. ассемблерный код – это обычные текстовые файлы. Мы рекомендуем пользоваться встроенным редактором Far Manager (). Думаем, что не следует заострять внимание на том, как пользоваться данными программами, тем более, что это выходит за рамки настоящей книги.

  • Потребуется ассемблер — программа, которая переводит ассемблерные инструкции в машинный код. Это может быть MASM.EXE® (ML.EXE) компании Microsoft, TASM.EXE® компании Borland, FASM® или другие. Для программирования на ассемблере под Windows потребуется MASM32®. Скачать всё это можно бесплатно на сайте автора – http://Kalashnikoff.ru. В принципе, большой разницы для наших примеров это пока не имеет, за исключением передачи параметров в командной строке при ассемблировании. Мы будем использовать MASM 6.11 — Macro Assembler® от Microsoft версии 6.11 для программ в первой части книги и MASM32 – для программ во второй части, что и вам советуем. Если в процессе ассемблирования возникают ошибки, то обращайтесь к приложению 2 "Ошибки при ассембли­ровании" или к нашим экспертам на http://RFpro.ru.
  • Настоятельно рекомендуем иметь отладчик (AFD®, SoftIce®, CodeView®). Он необходим для отладки программы и в целом для демонстрации ее работы. Предпочтительно использовать AFD или CodeView для начинающих и SoftIce для уже имеющих опыт программирования.


  • В будущем вам, безусловно, понадобится дизассемблер, который необходим для перевода машинного кода на язык ассемблера. Мы предпочитаем IDA®, как один из самых мощных и удобных в пользовании.

Можно также скачать минимальный, но достаточный для изучения настоящего курса набор программного обеспечения по следующему адресу: http://www.Kalashnikoff.ru.
4. Как построены главы книги?

  • Ответы на часто задаваемые вопросы.

  • Заметки, дополнительные примеры и алгоритмы.

  • Объяснение новой темы (теория).

  • Примеры программ на ассемблере (практика).


Вы сможете самостоятельно написать простую программу уже после прочтения главы 1. Надеемся, что изучать язык будет интересней, если мы сра­зу перейдем к практической части, изучая параллельно теорию. Попутно отметим, что данная книга рассчитана, в первую очередь, на людей, которые ни разу не писали программы ни на ассемблере, ни на каком другом языке программирования. Конечно, если вы уже знакомы с Basic, Pascal, C или каким-либо иным языком, то это только на пользу вам. Тем не менее все новые термины будут подробно объясняться.

Также следует отметить, что для полного изучения курса необходимы минимальные пользовательские знания операционной системы MS-DOS, т. к. ассемблирование программ из данной книги следует выполнять именно в консоле (Пуск – Выполнить – cmd). Однако вы также можете работать в специальных файловых оболочках типа Far Manager, Windows Commander, Total Commander и т.п.


5. Какие темы будут рассмотрены в настоящей книге?

  • Двоичная и шестнадцатеричная системы счисления.

  • Основные команды процессоров Intel 8086, 80286, 80386, 80486.

  • 16- и 32-разрядные регистры.

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

  • Сегментация памяти в реальном режиме.

  • Расширенная память (XMS-память).

  • Прямая работа с видеоадаптером.

  • Режимы CGA, EGA, VGA (кратко).

  • Управление клавиатурой на уровне прерываний.

  • Основные функции BIOS (ПЗУ) и MS-DOS.

  • Работа с дисками, каталогами и файлами.

  • Управление последовательным портом.

  • Высокоуровневая оптимизация программ.

  • Структура и особенности программирования в MS-DOS и Windows;

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

На все ваши вопросы по ассемблеру, а также по многим другим темам, ответят наши эксперты на портале профессионалов http://RFpro.ru. Стоит отметить, что на упомянутом портале вы сможете:

  • установить контакт с начинающими программистами и профессионалами на ассемблере;

  • пообщаться в реальном времени с автором данной книги;

  • принять участие в реальных встречах, чтобы лично познакомиться с профессионалами и экспертами.

Зарегистрируйтесь прямо сейчас на портале http://RFpro.ru и вступайте в наш клуб профессионалов!

В главе 1 мы рассмотрим шестнадцатеричную систему счисления и пример простейшей программы на ассемблере, традиционно называемой "Hello, world!".



Приятного вам изучения!


ГЛАВА 01

Шестнадцатеричная система счисления.


Для написания программ на Ассемблере, необходимо разобраться с шестнадцатеричной системой счисления. Ничего сложного в ней нет. Мы используем в жизни десятичную систему. Уверен, что вы все ее знаете, поэтому я постараюсь объяснить шестнадцатеричную систему, проводя аналогию с десятичной.


Итак, в десятичной системе если мы к какому-нибудь числу справа добавим нуль, то это число увеличится в 10 раз. Например:


1 х 10 = 10

10 х 10 = 100

100 х 10 = 1000

и т.д…


В этой системе мы используем цифры от 0 до 9, т.е. десять разных цифр (собственно, поэтому она и называется десятичная).


В шестнадцатеричной системе мы используем, соответственно, шестнадцать "цифр". Я специально написал слово "цифр" в кавычках, т.к. в ней используются не только цифры. Да и в самом деле, как так? Объясняю: от 0 до 9 мы считаем так же, как и в десятичной, а вот дальше будет так: A, B, C, D, E, F. Число F, как не трудно посчитать, будет равно 15 в десятичной системе (см. Таблицу 1).


Десятичное число

Шестнадца-теричное число

Десятичное число

Шестнадца-теричное число

0

1

2

3

4



8

9

10


11

12

13

14

15

16

17



0

1

2

3

4



8

9

A

B

C

D

E

F

10

11



26

27

28

29

30



158

159

160

161

162



254

255

256

257



1A

1B

1C

1D

1E



9E

9F

A0

A1

A2



FE

FF

100

101



Таблица 1. Десятичная и шестнадцатеричная системы.


Т.о., если мы к какому-нибудь числу в шестнадцатеричной системе добавим справа нуль, то это число увеличится в 16 раз.


Пример 1:


1 х 16 = 10

10 х 16 = 100

100 х 16 = 1000

и т.д…

Вы смогли отличить в Примере 1 шестнадцатеричные числа от десятичных? А из этого ряда: 10, 12, 45, 64, 12, 8, 19? Это могут быть как шестнадцатеричные, так и десятичные. Для того чтобы не было путаницы, и компьютер смог бы однозначно отличить одни числа от других, в Ассемблере принято после шестнадцатеричного числа ставить символ h или H (H – это сокращение от англ. hexadecimal (шестнадцатеричное). Для краткости его иногда называют просто Hex). А после десятичного ничего не ставить. Т.к. числа от 0 до 9 в обеих системах имеют одинаковые значения, то числа, записанные как 5 и 5h одно и тоже.



Т.о. Пример 1 (см. выше) правильнее будет записать так:


1 х 16 = 10h

10h x 16 = 100h

100h x 16 = 1000h


Либо так:


1h x 10h = 10h

10h x 10h = 100h

100h x 10h = 1000h


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


Итак, подведем итог. Шестнадцатеричная система счисления состоит из 10 цифр (от 0 до 9) и 6 букв латинского алфавита (A, B, C, D, E, F). Если к какому-нибудь числу в шестнадцатеричной системе добавим справа нуль, то это число увеличится в 16 раз. Очень важно уяснить данную тему, так как мы будем постоянно использовать ее при написании программ.


Теперь немного о том, как я буду строить примеры на Ассемблере. Не совсем удобно приводить их сплошным текстом в книге, поэтому сперва будет идти сам код программы с пронумерованными строками, а сразу же после него – объяснения и примечания.


Примерно так:




(01) mov ah,9

(02) mov al,8



(15) mov dl,5Ah




Номера строк ставятся только в наших примерах! При наборе программ в текстовом редакторе номера ставить НЕ нужно!


Объяснения:

В строке (01) мы делаем то-то, а в строке (15) то-то.


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

И еще. Строчные и ПРОПИСНЫЕ символы в Ассемблере не различаются. Записи вида:


mov ah,9

и

MOV AH,9

Ассемблером воспринимаются одинаково. Можно, конечно, заставить Ассемблер различать строчные и ПРОПИСНЫЕ символы, но мы пока этого делать не будем. Для удобства чтения программы лучше всего операторы печатать строчными буквами, а названия подпрограмм и меток начинать с прописной.


Наша первая программа



Итак, переходим к нашей первой программе (/001/PROG01.ASM):


(01) CSEG segment

(02) org 100h

(03)

(04) Begin:

(05)

(06) mov ah,9

(07) mov dx,offset Message

(08) int 21h

(09)

(10) int 20h

(11)

(12) Message db 'Hello, world!$'

(13) CSEG ends

(14) end Begin


Еще раз подчеркну: номера строк в скобках ставить не нужно! На сайте

http://www.Kalashnikoff.ru находится архив всех файлов-приложений в DOS-формате, которые я рекомендую вам загрузить. В скобках указывается имя файла из архива файлов-приложений (в данном случае – /001/PROG01.ASM, где 001 – каталог, PROG01.ASM – имя ассемблерного файла в DOS-формате).

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



Для того, чтобы объяснить все операторы данного примера, нам потребуется несколько глав. Поэтому описание некоторых команд мы просто опустим на данном этапе. Просто считайте, что так должно быть. В самое ближайшее время мы рассмотрим эти операторы подробно. Итак, строки с номерами (01), (02) и (13) вы просто игнорируете.


Строки (03), (05), (09) и (11) остаются пустыми. Это делается для наглядности. Ассемблер их будет просто опускать.

Теперь перейдем к рассмотрению остальных операторов. Со строки (04) начинается код программы. Это метка, указывающая Ассемблеру на начало кода. В строке (14) стоят операторы end Begin (Begin – от англ. – начало; end –конец). Это конец программы. Вообще вместо слова Begin можно было бы использовать что-нибудь другое. Например, Start:. В таком случае, нам пришлось бы и завершать программу End Start (14).



Строки (06) – (08) выводят на экран сообщение "Hello, world!". Здесь придется вкратце рассказать о регистрах процессора (более подробно эту тему мы рассмотрим в последующих главах).


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


Например:


Если мы хотим сложить два числа, то в математике запишем так:


A=5

B=8

C=A+B.


A, B и C – это своего рода регистры (если говорить о компьютере), в которых могут храниться некоторые данные. А=5 следует читать как: "Присваиваем А число 5".


Для присвоения регистру какого-нибудь значения, в Ассемблере существует оператор mov (от англ. move – в данном случае – загрузить). Строку (06) следует читать так: "Загружаем в регистр AH число 9" (проще говоря, присваиваем AH число 9). Ниже рассмотрим, зачем это надо.


В строке (07) загружаем в регистр DX адрес сообщения для вывода (в данном примере это будет строка "Hello, world!$").


Далее, в строке (08), вызываем прерывание MS-DOS, которое и выведет нашу строку на экран. Прерывания будут подробно рассматриваться в последующих главах. Здесь я скажу несколько слов.


Прерывание MS-DOS – это своего рода подпрограмма (часть MS-DOS), которая находится постоянно в памяти и может вызываться в любое время из любой программы.


Рассмотрим вышесказанное на примере (мелким шрифтом выделим примечания):

___________

Программа (алгоритм) сложения двух чисел




НачалоПрограммы


A=5 в переменную A заносим значение 5

B=8 в переменную B значение 8

ВызовПодпрограммы Сложение


теперь С равно 13


A=10 то же самое, только другие числа

B=25

ВызовПодпрограммы Сложение


теперь С равно 35

КонецПрограммы выходим из программы




Подпрограмма Сложение


C=A+B

ВозвратИзПодпрограммы возвращаемся в то место, откуда вызывали

КонецПодпрограммы


___________


В данном примере мы дважды вызвали подпрограмму Сложение, которая сложила два числа, переданные ей в переменных A и B. Результат помещается в переменную С. Когда вызывается подпрограмма, компьютер запоминает с какого места она была вызвана, а затем, когда закончила работу подпрограмма, компьютер возвращается в то место, откуда она вызывалась. Т.о., можно вызывать подпрограммы неопределенное количество раз с любого места.


При выполнении строки (08) программы на Ассемблере мы вызываем подпрограмму (в данном случае – это называется прерывание), которая выводит на экран строку. Для этого мы, собственно, и помещаем необходимые значения в регистры. Всю работу (вывод строки, перемещение курсора) берет на себя подпрограмма. Строку (08) следует читать так: вызываем двадцать первое прерывание (int – от англ. interrupt – прерывание). Обратите внимание, что после числа 21 стоит буква h. Это, как мы уже знаем, шестнадцатеричное число (33 в десятичной системе). Конечно, нам ничего не мешает заменить строку int 21h на int 33. Программа будет работать корректно. Просто в Ассемблере принято указывать номера прерываний в шестнадцатеричной системе.


В строке (10) мы, как вы уже догадались, вызываем прерывание 20h. Для вызова данного прерывания не нужно указывать какие-либо значения в регистрах. Оно выполняет только одну задачу: выход из программы (выход в DOS). В результате выполнения прерывания 20h, программа вернется туда, откуда ее запускали (загружали, вызывали). Например, в Norton Commander или DOS Navigator.

Строка (12) содержит сообщение для вывода. Первое слово (message – сообщение) – название сообщения. Оно может быть любым (например, mess или string и пр.). Обратите внимание на строку (07), в которой мы загружаем в регистр DX адрес нашего сообщения.



Можно создать еще одну строку, которую назовем Mess2. Затем, начиная со строки (09) вставим следующие команды:




(09) mov ah,9

(10) mov dx,offset Mess2

(11) int 21h

(12) int 20h

(13) Message db 'Hello, world!$'

(14) Mess2 db 'Это Я!$'

(15) CSEG ends

(16) end Begin


Надеюсь, вы догадались, что произойдет.


Обратите внимание на последний символ в строках Message и Mess2 – $. Он указывает на конец строки. Если мы его уберем, то 21h прерывание продолжит вывод до тех пор, пока не встретится где-нибудь в памяти символ $. На экране, помимо нашей строки в самом начале, мы увидим "мусор" (разные символы, которых в строке вовсе нет).


Теперь ассемблируйте программу. Как это сделать – написано в Приложении № 01. Заметьте, что мы создаем пока только *.com-файлы, а не *.exe! Для того чтобы получить *.com-файл, нужно указать определенные параметры ассемблеру (MASM / TASM) в командной строке.


При возникновении ошибок в процессе ассемблирования – обращайтесь к Приложению № 02, где рассматриваются типичные ошибки при ассемблировании программ.


Если у вас есть отладчик (AFD, CV), то можно (и даже нужно!) запустить ее под отладчиком. Это поможет вам лучше понять структуру и принцип работы Ассемблера, а также продемонстрирует реальную работу программы.


Целью настоящей главы не было разобраться подробно с каждым оператором. Это невозможно, т.к. у вас еще недостаточно знаний. Я полагаю, что, прочитав 3-4 главы, вы поймете принцип и структуру программы на Ассемблере.

Может быть, вам показался язык Ассемблера чрезвычайно сложным, но это, поверьте, с первого взгляда. Вы должны научиться строить алгоритм программы на Ассемблере в голове, а для этого нужно будет написать несколько программ самостоятельно, опираясь на информацию из данной книги. Я буду постепенно учить вас мыслить структурой Ассемблера, учить составлять алгоритмы, программы, используя операторы языка. После изучения очередной главы, вы будете чувствовать, что постепенно начинаете осваивать Ассемблер, будет становиться все проще и проще…



Например, если вы знакомы с Бейсиком, то, ставя перед собой задачу написать программу, выводящую 10 слов "Привет", вы будете использовать операторы FOR, NEXT, PRINT и пр., которые тут же появятся в ваших мыслях. Вы строите определенный алгоритм программы из этих операторов, который в какой-то степени применим только к Бейсику. То же самое и с Ассемблером. При постановке задачи написать ту или иную программу, вы мысленно создаете алгоритм, который применим к Ассемблеру и только, т.к. языков, похожих на Ассемблер, просто не существует. Моя задача – научить вас создавать в уме алгоритмы, применимые к Ассемблеру, т.е., образно говоря, научить "мыслить на Ассемблере".


В следующей главе мы подробно рассмотрим регистры процессора и напишем еще одну простенькую программку.

ГЛАВА 02

Регистры микропроцессоров 8086 – 80186.


В прошлой главе мы рассмотрели с вами шестнадцатеричную систему счисления и простую программу вывода строки на экран.

В данной главе рассматриваются регистры процессоров 8086 / 8088 / 80186.


Регистр, как мы уже рассматривали раньше, – это, просто говоря, специально отведенная память для временного хранения каких-то данных, переменная.


Микропроцессоры 8086 – 80186 имеют 14 регистров. В прошлой главе мы встретили два из них: AH и DX. В Таблицах № 1, 2 и 3 приведены списки всех регистров, кроме IP и регистра флагов, которые со временем будут рассматриваться отдельно:


EAX

EBX

AX

BX


CX

DX

AH

AL

BH

BL

CH

CL

DH

DL

Таблица № 1. Регистры данных



SI

DI

BP

SP

Таблица № 2. Регистры-указатели



CS

DS

ES

SS

Таблица № 3. Сегментные регистры


Регистры данных (Таблица № 1)


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

В верхнем ряду Таблицы (AX (аккумулятор), BX (база), CX (счетчик), DX (регистр данных)) находятся шестнадцатиразрядные регистры, которые могут хранить числа от 0 до 65.535 (от 0h до FFFFh в шестнадцатеричной системе (вспоминаем прошлую главу)). Под ними идет ряд восьмиразрядных регистров (AH, AL, BH, BL, CH, CL, DH, DL), которые могут хранить максимальное число 255 (FFh). Это половинки (старшая или младшая) шестнадцатиразрядных регистров.



Например:

Мы уже знаем оператор mov, который предназначен для загрузки числа в регистр. Чтобы присвоить, к примеру, регистру AL число 35h, нам необходимо записать так:


mov al,35h


а регистру AX число 346Ah – так:


mov ax,346Ah


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


Например, следующие записи будут ошибочны:


mov ah,123hмаксимум FFh

mov bx,12345hмаксимум FFFFh

mov dl,100hмаксимум FFh


Здесь надо отметить, что если шестнадцатеричное число начинается не с цифры (напр.: 12h), а с буквы (A-F) (напр.: С5h), то перед таким числом ставится нуль: 0C5h. Это необходимо для того, чтобы программа-ассемблер могла отличить, где шестнадцатеричное число, а где метка. Ниже мы рассмотрим это на примере.


Допустим, мы выполнили команду mov ax,1234h. В этом случае в регистре AH будет находиться число 12h, а в регистре AL – 34h. Т.е. AH, AL, BH, BL, CH, CL, DH и DL – это младшие (Low) или старшие (High) половинки шестнадцатиразрядных регистров (см. Таблицу № 4).


Команда:

Результат:

mov ax,1234h


mov bx,5678h


mov cx,9ABCh


mov dx,0DEF0h

AX = 1234h, AH = 12h, AL = 34h


BX = 5678h, BH = 56h, BL = 78h

CX = 9ABCh, CH = 9Ah, CL = 0BCh



DX = 0DEF0h, DH = 0DEh, DL = 0F0h


Таблица № 4. Результаты выполнения различных команд


Рассмотрим еще два оператора: add и sub.


Оператор add имеет следующий формат (в последствии мы всегда будем оформлять новые команды в такие таблицы):


Команда

Перевод

Назначение

Процессор

ADD приемник, источник

ADDition – сложение

Сложение

8086


В столбце Команда будет описываться новая команда и ее применение. В столбце Назначение – что выполняет или для чего служит данная команда, а в столбце Процессор – модель (тип) процессора с которого она поддерживается. Перевод – с какого английского слова образован оператор и его перевод. В данном примере – это 8086 процессор, но работать команда будет, естественно и на последующих, более современных процессорах (80286, 80386 и т.д.).


Команда add производит сложение двух чисел.


Примеры:


mov al,10  загружаем в регистр AL число 10

add al,15  AL = 25; AL – приемник, 15 – источник


mov ax,25000  загружаем в регистр AX число 25000

add ax,10000  AX = 35000; AX – приемник, 10000 – источник


mov cx,200  загружаем в регистр CX число 200

mov bx,760  а в регистр BX – 760


add cx,bx  CX = 960, BX = 760 (BX не меняется); CX –

приемник, BX – источник



Команда

Перевод

Назначение

Процессор

SUB приемник, источник

SUBtraction – вычитание

Вычитание

8086


Команда sub производит вычитание двух чисел.


Примеры:


mov al,10

sub al,7  AL = 3; AL – приемник, 7 – источник


mov ax,25000

sub ax,10000  AX = 15000; AX – приемник, 10000 – источник


mov cx,100

mov bx,15

sub cx,bx  CX = 85, BX = 15 (BX не меняется!); CX –

приемник, BX – источник


Это интересно.


Следует отметить, что Ассемблер максимально быстрый язык. Можно посчитать сколько раз за одну секунду процессор сможет сложить два любых числа от 0 до 65535.


Каждая команда процессора выполняется определенное количество тактов. Когда говорят, что тактовая частота процессора 100Mhz, то это значит, что за секунду проходит 100 миллионов тактов.


Чтобы сложить два числа в Ассемблере нужно выполнить следующие команды:




mov ax,2700

mov bx,15000

add ax,bx



В результате выполнения данных инструкций, в регистре AX будет число 17700, а в регистре BX – 15000. Команда add ax,bx выполняется за один такт на процессоре 80486. Получается, что компьютер 486 DX2-66Mhz за одну секунду сложит два любых числа (от 0 до 0FFFFh) 66 миллионов (!) раз! А еще называют "четверку" медленной!..



Регистры-указатели (Таблица № 2)


Регистры SI (индекс источника) и DI (индекс приемника) используются в строковых операциях. Регистры BP и SP необходимы при работе со стеком. Мы их будем подробно рассматривать в последующих главах.


Сегментные регистры (Таблица № 3)


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

_________________


Давайте изучим еще несколько команд в данной главе:


Команда

Перевод

Назначение

Процессор

INC приемник

Increment – инкремент

Увеличение на единицу

8086


Команда inc увеличивает на единицу регистр. Она эквивалентна команде:


ADD источник, 1


только выполняется быстрее на старых компьютерах (до 80486) и занимает меньше байт.


Примеры:


mov al,15

inc al  теперь AL = 16 (эквивалентна add al,1)


mov dh,39h

inc dh  DH = 3Ah (эквивалентна add dh,1)


mov cl,4Fh

inc cl  CL = 50h (эквивалентна add cl,1)


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


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



Управлять клавиатурой позволяет прерывание 16h. Это прерывание BIOS (ПЗУ), а не MS-DOS (как 21h). Его можно вызывать даже до загрузки операционной системы, в то время, как прерывание 21h доступно только после загрузки IO.SYS / MSDOS.SYS (т.е. определенной части ОС MS-DOS).


Чтобы остановить программу до нажатия любой клавиши следует вызвать функцию 10h прерывания 16h. Вот как это выглядит (после стрелки () идет комментарий):


mov ah,10h  в AH всегда указывается номер функции

int 16h  вызываем прерывание 16h – сервис работы с

клавиатурой BIOS (ПЗУ)


После нажатия на любую клавишу, компьютер продолжит выполнять программу, а регистр AX будет содержать код клавиши, которую нажал пользователь.


Следующая программа (/002/PROG02.asm) выводит на экран сообщение и ждет нажатия любой клавиши (равнозначна команде "PAUSE" в *.bat файлах):


(01) CSEG segment

(02) org 100h

(03) Start:

(04)

(05) mov ah,9

(06) mov dx,offset String

(07) int 21h

(08)

(09) mov ah,10h

(10) int 16h

(11)

(12) int 20h

(13)

(14) String db 'Нажмите любую клавишу…$'

(15) CSEG ends

(16) end Start


Строки с номерами (01), (02) и (15) пока опускаем. В строках (05) – (07), как вы уже знаете, выводим на экран сообщение. Затем (строки (09) – (10)) ждем нажатия клавиши. И, наконец, строка (12) выходит из нашей программы.


Мы уже знаем операторы inc, add и sub. Можно поэкспериментировать с вызовом прерывания. Например, так:




mov ah,0Fh

inc ah

int 16h




Это позволит вам лучше запомнить новые операторы.

В следующей главе рассмотрим двоичную систему счисления, основы сегментации памяти и сегментные регистры. Напишем интересную программку.

ГЛАВА 03

Двоичная система счисления.

Бит и байт.


В предыдущей главе мы рассмотрели с вами шестнадцатеричную систему счисления и простую программу вывода строки на экран.


В настоящей главе мы рассмотрим основополагающие сведения по программированию на языке Ассемблера. Необходимо тщательно разобраться в каждом предложении и уяснить двоичную систему счисления, понять принцип сегментации памяти в реальном режиме. Мы также рассмотрим операторы Ассемблера, которые не затрагивали в примерах из предыдущей главы. Сразу отмечу, что это один из самых сложных разделов данной книги. Я пытался объяснить все это как можно проще, не используя сложных определений и терминов. Если что-то не поняли – не пугайтесь! Со временем все станет на свои места. Более того, мне можно задавать вопросы по адресу, указанному в Предисловии.


Если вы полностью разберетесь во всем в данной главе, то считайте, что базу Ассемблера вы изучили. Со следующей главы будем изучать язык намного интенсивней.


Для того чтобы лучше понять сегментацию памяти, нам нужно воспользоваться отладчиком. Я работал с CodeView (CV.EXE) и AFD Pro (AFD.EXE).


Допустим, вы написали программу на ассемблере и назвали ее PROG03.ASM. Сассемблировав, вы получили файл PROG03.COM. Тогда, чтобы запустить ее под отладчиком CodeView / AFD, необходимо набрать в командной строке MS-DOS следующее:


CV.EXE prog03.com

либо:

AFD.EXE prog03.com

Итак, вдохните глубже и – вперед!




Рассмотрим, как в памяти компьютера хранятся данные.

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



Предположим, что:

нуль будет обозначаться как 0000 (четыре нуля),

Один - 0001,

Два - 0010 (т.е. правую единицу заменяем на 0, а вторую устанавливаем в 1).

Далее так:

Три - 0011

Четыре - 0100

Пять - 0101

Шесть - 0110

Семь - 0111

Восемь - 1000

Девять - 1001

и т.д.


Уловили принцип? "0" и "1" – это т.н. биты. Один бит, как вы уже заметили, может быть нулем или единицей, т.е. размагничена или намагничена та или иная дорожка ("0" и "1" – это условное обозначение). Присмотревшись, можно обнаружить, что каждый следующий установленный бит (начиная справа) увеличивает число в два раза: 0001 в нашем примере – единица; 0010 – два; 0100 – четыре; 1000 – восемь и т.д. Это и есть т.н. двоичная форма представления данных.


Т.о., чтобы обозначить числа от 0 до 9, нам нужно четыре бита (хоть они и не до конца использованы. Можно было бы продолжить: десять – 1010, одиннадцать – 1011…, пятнадцать – 1111).


Компьютер хранит данные в памяти именно так. Для обозначения какого-нибудь символа (цифры, буквы, запятой, точки…) в компьютере используется определенное количество бит. Компьютер "распознает" 256 (от 0 до 255) различных символов по их коду. Этого достаточно, чтобы вместить все цифры (0 – 9), буквы латинского алфавита (a – z, A – Z), русского (а – я, А – Я) и др. Для представления символа с максимально возможным кодом (255) нужно 8 бит. Эти 8 бит называются байтом. Т.о. один любой символ – это всегда 1 байт (см. рис. 1).


0

1

0


1

1

0

1

0

Р

Н

Р

Н

Н

Р

Н

Р

Рис. 1. Один байт с кодом символа "Z"

(символы "Н" и "Р" обозначают Намагничено или Размагничено соответственно)


Можно элементарно проверить. Создайте в текстовом редакторе файл с любым именем и запишите в нем один символ, например, "М" (но не нажимайте Enter!). Если вы посмотрите его размер, то файл будет равен 1 байту. Если ваш редактор позволяет смотреть файлы в шестнадцатеричном формате, то вы сможете узнать и код сохраненного символа. В данном случае – буква "М" имеет код 4Dh в шестнадцатеричной системе, которую мы уже знаем или 1001101 в двоичной.


Т.о. слово "диск" будет занимать 4 байта или 4*8 = 32 бита. Как вы уже поняли, компьютер хранит в памяти не сами буквы (символы) этого слова, а последовательность "единичек" и "ноликов".

"Почему же тогда на экране мы видим набор символов (текст, предложения, слова), а не "единички-нолики"? – спросите вы. Чтобы удовлетворить ваше любопытство, я забегу немного вперед и скажу, что всю работу по выводу самогó символа на экран (а не битов) выполняет видеокарта (видеоадаптер), которая находится в вашем компьютере. И если бы ее не было, то мы, естественно, ничего бы не видели, что у нас отображено на экране.



В Ассемблере после двоичного числа всегда должна стоять буква "b". Это нужно для того, чтобы при ассемблировании программы Ассемблер смог отличать десятичные, шестнадцатеричные и двоичные числа. Например: 10 – это "десять", 10h – это "шестнадцать" а 10b – это "два" в десятичной системе.


Т.о. в регистры можно загружать двоичные, десятичные и шестнадцатеричные числа.


Например:



mov ax,20

mov bh,10100b

mov cl,14h




В результате в регистрах AX, BH и CL будет находиться одно и тоже число, только загружаем мы его, используя разные системы счисления. Компьютер же будет хранить его в двоичном формате (как в регистре BH).


Итак, подведем итог. В компьютере вся информация хранится в двоичном формате (двоичной системе) примерно в таком виде: 10101110 10010010 01111010 11100101 (естественно, без пробелов. Для удобства я разделил байты). Восемь бит – это один байт. Один символ занимает один байт, т.е. восемь бит. По-моему, ничего сложного. Очень важно уяснить данную тему, так как мы будем постоянно пользоваться двоичной системой, и вам необходимо знать ее на "отлично". В принципе, даже если что-то не совсем понятно, то – не отчаивайтесь! Со временем все станет понятно.


Как перевести двоичное число в десятичное:

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


Например:

Возьмем число 20. В двоичной системе оно имеет следующий вид: 10100b

Итак (начнем слева направо, считая от 4 до 0; число в нулевой степени всегда равно единице (вспоминаем школьную программу по математике)):

10100b = 1*16 + 0*8 + 1*4 + 0*2 + 0*1 = 20

--------------------------------------------------------------

16+0+4+0+0 = 20


Как перевести десятичное число в двоичное:

Можно делить его на два, записывая остаток справа налево:


20/2 = 10, остаток 0

10/2=5, остаток 0

5/2=2, остаток 1

2/2=1, остаток 0

1/2=0, остаток 1

В результате получаем: 10100b = 20



Как перевести шестнадцатеричное число в десятичное:

В шестнадцатеричной системе номер позиции цифры в числе соответствует степени, в которую надо возвести число 16:
8Ah = 8*16 + 10 (0Ah) = 138



В настоящий момент есть множество калькуляторов, которые могут считать и переводить числа в разных системах счисления. Например, калькулятор Windows, который должен быть в инженерном виде. Очень удобен калькулятор и в DOS Navigator'е. Если у вас есть он, то отпадает необходимость в ручном переводе одной системы в другую, что, естественно, упростит работу. Однако, знать этот принцип крайне важно!


Сегментация памяти в реальном режиме.


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


Теперь немного усложним задачу и разобьем предложение следующим образом (символом "_" обозначен пробел):


Пример № 1:


0000: Изучаем_

0010: сегменты_

0020: памяти

0030:


В слове "Изучаем" символ "И" стоит на нулевом месте; символ "з" на первом, "у" на втором и т.д. В данном случае мы считаем буквы, начиная с нулевой позиции, используя два числа. Назовем их сегмент и смещение. Тогда, символ "ч" будет иметь следующий адрес: 0000:0003, т.е. сегмент 0000, смещение 0003. Проверьте…

В слове "сегменты" будем считать буквы, начиная с десятого сегмента, но с нулевого смещения. Тогда символ "н" будет иметь следующий адрес: 0010:0005, т.е. пятый символ, начиная с десятой позиции: 0010 – сегмент, 0005 – смещение. Тоже проверьте…



В слове "память" считаем буквы, начиная с 0020 сегмента и также с нулевой позиции. Т.о. символ "а" будет иметь адрес 0020:0001, т.е. сегмент – 0020, смещение – 0001. Опять проверим…


Итак, мы выяснили, что для того, чтобы найти адрес нужного символа, необходимы два числа: сегмент и смещение внутри этого сегмента. В Ассемблере сегменты хранятся в сегментных регистрах: CS, DS, ES, SS (см. предыдущую главу), а смещения могут храниться в других (но не во всех). Не все так сложно, как кажется. Опять-таки, со временем вы поймете принцип.


  • Регистр CS служит для хранения сегмента кода программы (Code Segment – сегмент кода);




  • Регистр DS – для хранения сегмента данных (Data Segment – сегмент данных);




  • Регистр SS – для хранения сегмента стека (Stack Segment – сегмент стека);




  • Регистр ES – дополнительный сегментный регистр, который может хранить любой другой сегмент (например, сегмент видеобуфера).

_____________


Пример № 2:

Давайте попробуем загрузить в пару регистров ES:DI сегмент и смещение буквы "м" в слове "памяти" из Примера № 1 (см. выше). Вот как это запишется на Ассемблере:




(1) mov ax,0020

(2) mov es,ax

(3) mov di,2




Теперь в регистре ES находится сегмент с номером 20, а в регистре DI – смещение к букве (символу) "м" в слове "памяти". Проверьте, пожалуйста…


Здесь стоит отметить, что загрузка числа (т.е. какого-нибудь сегмента) напрямую в сегментный регистр запрещена. Поэтому мы в строке (1) загрузили сегмент в AX, а в строке (2) загрузили в регистр ES число 20, которое находилось в AX:


mov ds,15ошибка!

mov ss,34hошибка!



Когда мы загружаем программу в память, она автоматически располагается в первом свободном сегменте. В файлах типа *.com все сегментные регистры автоматически инициализируются для этого сегмента (устанавливаются значения равные тому сегменту, в который загружена программа). Это можно проверить при помощи отладчика. Если, например, мы загружаем программу типа *.com в память, и компьютер находит первый свободный сегмент с номером 5674h, то сегментные регистры будут иметь следующие значения:


CS = 5674h

DS = 5674h

SS = 5674h

ES = 5674h


Иначе говоря: CS = DS = SS = ES = 5674h


Код программы типа *.com должен начинаться со смещения 100h. Для этого мы, собственно, и ставили в наших прошлых примерах программ оператор ORG 100h, указывая Ассемблеру при ассемблировании использовать смещение 100h от начала сегмента, в который загружена наша программа (позже мы рассмотрим, почему так). Сегментные же регистры, как я уже говорил, автоматически принимают значение того сегмента, в который загрузилась наша программа.


Пара регистров CS:IP задает текущий адрес кода. Теперь рассмотрим, как все это происходит на конкретном примере:


Пример № 3.


(01) CSEG segment

(02) org 100h

(03) _start:

(04) mov ah,9

(05) mov dx,offset My_name

(06) int 21h

(07) int 20h

(08) My_name db 'Dima$'

(09) CSEG ends

(10) end _start


Итак, строки (01) и (09) описывают сегмент:


CSEG (даем имя сегменту) segment (оператор Ассемблера, указывающий, что имя CSEG – это название сегмента);


CSEG ends (END Segment – конец сегмента) указывает Ассемблеру на конец сегмента.

Строка (02) сообщает, что код программы (как и смещения внутри сегмента CSEG) необходимо отсчитывать с 100h. По этому адресу в память всегда загружаются программы типа *.com.



Запускаем программу из Примера № 3 в отладчике. Допустим, она загрузилась в свободный сегмент 1234h. Первая команда в строке (04) будет располагаться по такому адресу:


1234h:0100h (т.е. CS = 1234h, а IP = 0100h) (посмотрите в отладчике на регистры CS и IP).


Перейдем к следующей команде (в отладчике CodeView нажмите клавишу F8, в AFD – F1, в другом – посмотрите, какая клавиша нужна; будет написано что-то вроде "F8-Step" или "F7-Trace"). Теперь вы видите, что изменились следующие регистры:


  • AX = 0900h (точнее, AH = 09h, а AL = 0, т.к. мы загрузили командой mov ah,9 число 9 в регистр AH, при этом не трогая AL. Если бы AL был равен, скажем, 15h, то после выполнения данной команды AX бы равнялся 0915h)




  • IP = 102h (т.е. указывает на адрес следующей команды. Из этого можно сделать вывод, что команда mov ah,9 занимает 2 байта: 102h – 100h = 2).


Следующая команда (нажимаем клавишу F8 / F1) изменяет регистры DX и IP. Теперь DX указывает на смещение нашей строки ("Dima$") относительно начала сегмента, т.е. 109h, а IP равняется 105h (т.е. адрес следующей команды). Нетрудно посчитать, что команда mov dx,offset My_name занимает 3 байта (105h – 102h = 3).


Обратите внимание, что в Ассемблере мы пишем:


mov dx,offset My_name


а в отладчике видим следующее:


mov dx,109 ;(109 – шестнадцатеричное число, но CodeView и многие другие отладчики символ 'h' не ставят. Это надо иметь в виду).


Почему так происходит? Дело в том, что при ассемблировании программы, программа-ассемблер (MASM / TASM) подставляет вместо offset My_name реальный адрес строки с именем My_name в памяти (ее смещение). Можно, конечно, записать сразу:


mov dx,109h

Программа будет работать нормально. Но для этого нам нужно высчитать самим этот адрес. Попробуйте вставить следующие команды, начиная со строки (07) в Примере № 3:





(07) int 20h

(08) int 20h

(09) My_name db 'Dima$'

(10) CSEG ends

(11) end _start


Просто продублируем команду int 20h (хотя, как вы уже знаете, до строки (08) программа не дойдет).


Теперь ассемблируйте программу заново. Запускайте ее под отладчиком. Вы увидите, что в DX загружается не 109h, а другое число. Подумайте, почему так происходит. Это просто!


В окне "Memory" ("Память") отладчика CodeView (у AFD нечто подобное) вы должны увидеть примерно следующее:


1234

:

0000

CD 20 00 A0 00 9A F0 FE

= .a.

№1




№2

№3

№4


Позиция №1 (1234) – сегмент, в который загрузилась наша программа (может быть любым).


Позиция №2 (0000) – смещение в данном сегменте (сегмент и смещение отделяются двоеточием (:)).


Позиция №3 (CD 20 00 … F0 FE) – код в шестнадцатеричной системе, который располагается с адреса 1234:0000.


Позиция №4 (= .a.) – код в ASCII (ниже рассмотрим), соответствующий шестнадцатеричным числам с правой стороны.

В Позиции №2 (смещение) введите значение, которое находится в регистре DX после выполнения строки (5). После этого в Позиции №4 вы увидите строку "Dima$", а в Позиции №3 – коды символов "Dima$" в шестнадцатеричной системе… Так вот что загружается в DX! Это не что иное, как АДРЕС (смещение) нашей строки в сегменте!



Но вернемся. Итак, мы загрузили в DX адрес строки в сегменте, который мы назвали CSEG (строки (01) и (09) в Примере № 3). Теперь переходим к следующей команде: int 21h. Вызываем прерывание DOS с функцией 9 (mov ah,9) и адресом строки в DX (mov dx,offset My_name).


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


Наше первое прерывание



Функция 09h прерывания 21h выводит строку на экран, адрес которой указан в регистре DX.


Вообще, любая строка, состоящая из ASCII символов, называется ASCII-строка. ASCII символы – это символы от 0 до 255 в DOS, куда входят буквы русского и латинского алфавитов, цифры, знаки препинания и пр. (полный список ASCII-символы различных кодировок смотрите в Приложении № 04).


Изобразим это в таблице (так всегда будем делать):


Функция 09h прерывания 21h – вывод строки символов на экран в текущую позицию курсора:

Вход:

AH = 09h

DX = адрес ASCII-строки символов, заканчивающийся '$'

Выход:

Ничего



В поле "Вход" мы указываем, в какие регистры что загружать перед вызовом прерывания, а в поле "Выход" – что возвращает функция. Сравните эту таблицу с Примером № 3.

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



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



Теперь интересная программка (/003/PROG03.ASM) для практики, которая выводит в верхний левый угол экрана веселую рожицу на синем фоне:


(01) CSEG segment

(02) org 100h

(03) _beg:

(04) mov ax,0B800h

(05) mov es,ax

(06) mov di,0

(07)

(08) mov ah,31

(09) mov al,1

(10) mov es:[di],ax

(11)

(12) mov ah,10h

(13) int 16h

(14)

(15) int 20h

(16)

(17) CSEG ends

(18) end _beg


Многие операторы вы уже знаете. Поэтому я буду объяснять только новые.


В данном примере, для вывода символа на экран, мы используем метод прямого отображения в видеобуфер. Что это такое – будет подробней рассмотрено в последующих главах.


В строках (04) и (05) загружаем в сегментный регистр ES число 0B800h, которое соответствует сегменту дисплея в текстовом режиме (запомните его!). В строке (06) загружаем в регистр DI нуль. Это будет смещение относительно сегмента 0B800h. В строках (08) и (09) в регистр AH заносится атрибут символа (31 – ярко-белый символ на синем фоне) и в AL – ASCII-код символа (01 – это рожица).


В строке (10) заносим по адресу 0B800:0000h (т.е. первый символ в первой строке дисплея – верхний левый угол) атрибут и ASCII-код символа (31 и 01 соответственно) (сможете разобраться?).


Обратите внимание на запись регистров в строке (10). Квадратные скобки ( [ ] ) указывают на то, что надо загрузить число не в регистр, а по адресу, который содержится в этом регистре (в данном случае, как уже отмечалось, – это 0B800:0000h).

Можете поэкспериментировать с данным примером. Только не меняйте пока строки (04) и (05). Сегментный регистр должен быть ES (можно, конечно, и DS, но тогда надо быть осторожным). Более подробно данный метод рассмотрим позже. Сейчас нам из него нужно понять принцип сегментации на практике.



Это интересно.


Следует отметить, что вывод символа прямым отображением в видеобуфер является самым быстрым. Выполнение команды в строке (10) занимает 3 – 5 тактов. Т.о. на Pentium-100Mhz можно за секунду вывести 20 миллионов(!) символов или чуть меньше точек на экран!


ПОСЛЕСЛОВИЕ


Уважаемый читатель! Вот вы и ознакомились с первой частью книги "Ассемблер? Это просто! Учимся программировать".


Целью настоящей части – было дать вам основы программирования на самом быстром, компактном и уникальном языке программирования – Ассемблере. Если вы усвоили как минимум 75 % материала из данной части, то, можно сказать, я добился того, чего хотел. Если же вы чувствуете, что многое непонятно, то попробуйте прочитать все еще раз с самого начала, воспользуйтесь отладчиком, напишите мне письмо, в котором изложите вопрос, предоставьте максимум возможной информации (какой программой-ассемблером пользуетесь, что вы делали для решения возникшей проблемы и пр.). Ответ гарантирую вам в самое ближайшее время. Тем не менее, впоследствии мы будем еще не раз возвращаться к материалу, который изложен в первой части. Если вы что-то не поняли сейчас, то, надеюсь, поймете со временем. Самое интересное и увлекательное – впереди!


В случае, если вопросов по пройденному материалу у вас не возникло, то можете смело приступать к изучению второй части книги – "От знакомства – к дружбе".


Буду также вам признателен за отзывы, критику, предложения относительно первой части, которые можно направлять мне по электронному адресу.


Для подписчиков рассылки

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


Вопросы следует задавать нашим экспертам. Форма подачи вопроса находится по адресу: http://RFpro.ru.

Следующий файл, который необходимо изучать – 004.htm, затем – 005.htm и т.д.


Увлекательного вам чтения!



(С) Авторское право принадлежит автору книги.

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