Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf

ВУЗ: Не указан

Категория: Книга

Дисциплина: Программирование

Добавлен: 16.02.2019

Просмотров: 29201

Скачиваний: 1689

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

 

Часть I. Знакомьтесь: ассемблер 

30 

 

Рис. 3.3. В отладчике выполнена команда mov dx,0109 

Почему так происходит? Дело в том, что в процессе обработки нашего файла, про-

грамма-ассемблер (MASM/TASM) подставляет вместо 

offset String

 реальный адрес 

строки с именем 

String

 в памяти (ее смещение). Можно, конечно, записать сразу: 

mov dx,109h 

Данная программа будет работать корректно. Но для этого нам нужно высчитать 

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

... 
(07)    int 20h 
(08)    int 20h 
(09) String db 'Test message$' 
(10) CSEG ends 
(11) end _start 

Просто  продублируем  команду 

int  20h

  (хотя,  как  вы  уже  знаете,  выполнение 

программы прекратится на строке (07)). Теперь ассемблируйте программу заново. 
Запускайте  ее  под  отладчиком.  В  качестве  демонстрации  воспользуемся  отладчи-
ком  CodeView,  при  этом  программа  может  загрузиться  в  другой  сегмент, т. к. от-
ладчики  имеют  разные  размеры  и,  соответственно,  занимают  разное  количество 
оперативной памяти. Пусть теперь сегмент, в который загрузилась наша програм-
ма,  будет  равен 

0A09h

.  Выполняйте  шаг  за  шагом  программу  до  тех  пор,  пока  не 

дойдете до загрузки смещения строки в регистр 

dx

. Вы увидите, что в 

dx

 загружа-

ется не 109h, а другое число. Подумайте, почему так происходит. 

А  мы  пока  подробней  рассмотрим,  что  находится  в  пределах  окна  отладчика 

CodeView. 

В окне Memory (Память) отладчика CodeView (у AFD нечто подобное) вы уви-

дите примерно следующее (табл. 3.2 и рис. 3.4). 


background image

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

31 

Таблица 3.2. Строка отладчика CodeView 

 

0A09 

0000 

CD 20 FF 9F 00 9A F0 FE 

= Я. 

 
Рассмотрим строку отладчика: 

 

позиция 1  (

0A09

) —  сегмент,  в  который  загрузилась  наша  программа  (может 

быть любым); 

 

позиция 2  (

0000

) —  смещение  в  данном  сегменте  (сегмент  и  смещение  отделя-

ются двоеточием (:)); 

 

позиция 3 (

CD 20 FF ... F0 FE

) — код в шестнадцатеричной системе, который 

располагается, начиная с адреса 

0A09:0000

 

позиция 4 (

=  Я.

) — код в ASCII (ниже рассмотрим), соответствующий шестна-

дцатеричным числам с правой стороны. 

 

Рис. 3.4. Сегмент, смещение, шестнадцатеричный и двоичный коды программы  

из листинга 3.1 в отладчике CodeView 

В позиции 2 (смещение) введите значение, которое будет находиться в регистре 

dx

 после выполнения строки (5). После этого в позиции 4 вы  увидите строку 

Test 

message$

,  а  в  позиции 3 —  коды  символов 

Test  message$

  в  шестнадцатеричной 

системе...  Так  вот  что  загружается  в 

dx

  (рис. 3.5)!  Это  не  что  иное,  как  АДРЕС 

(смещение) нашей строки в сегменте! 

Итак, мы загрузили в 

dx

 адрес строки в сегменте 

CSEG

 (строки (01) и (09) из лис-

тинга 3.1). Теперь переходим к следующей команде: 

int 21h

. Вызываем прерыва-

ние DOS с функцией 

9

 (

mov ah,9

) и адресом строки в 

dx

 (

mov dx,offset String

). 

Как уже упоминалось раньше, при вызове прерываний в программах в 

ah

 зано-

сится номер функции. Эти номера желательно запоминать (хотя бы часто исполь-


background image

 

Часть I. Знакомьтесь: ассемблер 

32 

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

 

Рис. 3.5. Строка Test message$ в памяти 

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

Функция 

09h

  прерывания 

21h

  выводит  строку  на  экран,  адрес  которой  указан  

в  регистре 

dx

.  Изобразим  это  в  табл. 3.3.  Далее  всегда  будем  описывать  функции  

с помощью таблиц. 

В столбце Вход мы указываем, в какие регистры что загружать перед вызовом 

прерывания, а в столбце Выход — что возвращает функция. Сравните эту таблицу 
с листингом 3.1. 

Таблица 3.3. Функция 

09h

 

прерывания 

21h

 

— вывод строки символов  

на экран в текущую позицию курсора 

Вход 

Выход 

ah = 09h 

dx 

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

Ничего 

 

3.3.1. Что такое ASCII? 

Вообще,  любая  строка,  состоящая  из  ASCII-символов,  называется  ASCII-

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


background image

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

33 

На  этом  мы  заканчиваем  рассматривать  сегментацию  памяти.  Если  и  остались 

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

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

Теперь интересная программка (\003\prog03.asm) для практики, которая выводит 

в левый верхний угол экрана веселую рожицу на синем фоне (листинг 3.2). 

Листинг 3.2. Программа для практики 

(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 

Многие операторы вы уже знаете. Поэтому рассмотрим только новые. 
В данном примере для вывода символа на экран (рис. 3.6), мы используем метод 

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

es

  число 

0B800h

,  которое  соответствует 

сегменту дисплея в текстовом режиме (запомните его!). В строке (06) загружаем в 
регистр 

di

 ноль. Это будет смещение относительно сегмента 

0B800h

. В строках (08)  

и (09) в регистр 

ah

 заносится атрибут символа (31 — ярко-белый символ на синем 

фоне) и в 

al

 — ASCII-код символа (01 — это "рожица"). 

В строке (10) заносим по адресу 

0B800:0000h

 (это первый символ в первой стро-

ке дисплея — в левом верхнем углу) атрибут и ASCII-код символа (31 и 01 соответ-


background image

 

Часть I. Знакомьтесь: ассемблер 

34 

ственно). Обратите внимание на форму записи оператора 

mov

  в  строке  (10).  Квад-

ратные  скобки 

[  ]

  указывают  на  то,  что  надо  загрузить  число  не  в  регистр,  а  по 

адресу,  который  содержится  в  этом  регистре  (в  данном  случае,  как  уже  отмеча-
лось, — это 

0B800:0000h

). 

 

Рис. 3.6. Результат выполнения программы Prog03.com 

Можете  поэкспериментировать  с  данным  примером.  Только  не  меняйте  пока 

строки  (04)  и  (05).  В  качестве  сегментного  регистра  оставим 

es

,  хотя  можно,  ко-

нечно, и 

ds

 использовать. Более подробно метод прямого отображения в видеобу-

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

 

Э

Т О   И Н Т Е Р Е С Н О

 

Ну  и  напоследок  интересный  факт.  Вывод  символа  прямым  отображением  в  видео-
буфер является самым быстрым из всех возможных способов  вывода символов или 
рисования точки на экране. Выполнение команды в строке (10) занимает 3—5 тактов. 
Таким образом, на Pentium 100 МГц можно за секунду вывести 20 миллионов (!) сим-
волов или чуть меньше точек на экран! 

3.5. Подведем итоги 

Уважаемые читатели! Вот вы и ознакомились с частью I книги. 
Итак, давайте подведем итог. В данной части мы рассмотрели следующее: 

 

шестнадцатеричную систему счисления; 

 

двоичную систему счисления; 

 

некоторые регистры микропроцессоров Intel 8086/8088/80186; 

 

основы сегментации памяти в реальном режиме; 

 

операторы ассемблера: 

 

org

 — с какого места отсчитывать смещение; 

 

mov

 — загрузка данных в регистр или память (переменную); 

 

add

 — сложение; 

 

sub

 — вычитание; 

 

inc

 — увеличение на единицу; 

 

int

 — вызов прерывания;