Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

2591

.pdf
Скачиваний:
2
Добавлен:
07.01.2021
Размер:
25.15 Mб
Скачать

 

 

Таблица 4.9

 

 

 

Код

Размер

Видеорежим

01

40 х 25

черно-белый режим в цветном адаптере

10

80 х 25

черно-белый режим в цветном адаптере

11

80 х 25

черно-белый режим в черно-белом адаптере

Программа, работающая с неизвестным типом монитора, может провеpить тип по регистру ax после int 11h и затем установить необходимый режим.

ah=02: Установка позиции курсора. Эта функция устанавливает курcор в любую позицию на экране в соответствии с координатами cтроки и столбца.

Номер страницы обычно равен 0, но может иметь значение от 0 до 3 при 80 столбцах на экране. Для установки позиции курсора необходимо занести в регистр ah значение 02, в регистр bh номер страницы и в регистр dx координаты строки и столбца:

mov ah,02

; установить положение курсора

mov bh,00

; страница 0

mov dh,строка

; строка

mov dl,столбец

; столбец

int 10h

; вызвать BIOS

ah =06: Прокрутка экрана вверх. В текстовом режиме установка в регистре al значения 00 приводит к полной прокрутке вверх всего экрана, очищая его пробелами. Установка ненулевого значения в регистре al определяет количество строк прокрутки экрана вверх. Верхние строки уходят с экрана, а чистые строки вводятся снизу. Следующие команды выполняют прокрутку всего экрана на одну строку:

mov ax,0601h

; прокрутить на одну строку вверх

mov bh,07

; атрибут: нормальный, черно-белый

mov cx,0000

; координаты от 00,00

mov dx,184Fh

;

до 24,79 (полный экран)

int 10h

; вызвать BIOS

Для прокрутки

любого количества

строк необходимо установить

соответствующее значение в регистре al. Регистр bh содержит атрибут для нормального или инвертированного отображения, мигания, установки цвета и т.д. Значения в регистрах cx и dx позволяют прокручивать любую часть экрана. Ниже объясняется стандартный подход к прокрутке:

1.Определить в элементе ROW (строка) значение 0 для установки строки положения курсора.

2.Выдать текст и продвинуть курсор на следующую строку.

3.Проверить, находится ли курсор на последней строке (cmp ROW,22).

41

4.Если да, то увеличить элемент ROW (inc ROW) и выйти.

5.Если нет, то прокрутить экран на одну строку и, используя ROW, переустановить курсор.

ah =07: Прокрутка экрана вниз. Для текстового режима прокрутка экрана вниз обозначает удаление нижних строк и вставка чистых строк сверху.

Регистр ah должен содержать 07, значения остальных регистpов аналогичны функции 06 для прокрутки вверх.

ah =08: Чтение атрибута/символа в текущей позиции курсора. Для чтения символа и байта-атрибута из дисплейного буфера, как в текстовом, так и в графическом режиме используются следующие команды:

mov ah,08 ; запрос на чтение атр./симв.

mov bh,00 ; страница 0 (для текстового режима)

int 10h ; вызвать BIOS

Данная функция возвращает в регистре al значение символа, а в ah – его атрибут. В графическом режиме функция возвращает 00h (означает, что это не ASCII-код). Так как эта функция читает только один cимвол, то для символьной строки необходима организация цикла.

ah=09: Вывод атрибута/символа в текущую позицию курсора. Для вывода на экран символов в текстовом или графическом режиме с установкой мигания, инвертирования и т.д. можно воспользоваться

следующими командами:

 

mov ah,09

; функция вывода

mov al,символ

; выводимый символ

mov bh,страница

; номер страницы (текстовый режим)

mov bl,атрибут

; атрибут или цвет

mov cx,повторение

; число повторений символа

int 10h

; вызвать BIOS

В регистр al должен быть помещен выводимый на экран символ. Значение в регистре cx определяет число повторений символа на экране. Вывод на экран последовательности различных символов требует организации цикла. Данная функция не перемещает курсор. В следующем примере на экран выводится пять мигающих "сердечек" в

инвертированном виде:

 

mov ah,09

; функция вывода

mov al,03h

; черви (карточная масть)

mov bh,00

; страница 0 (текст. режим)

mov bl,0F0h

; мигание, инверсия

mov cx,05

; пять раз

int 10h

; вызвать BIOS

 

42

Втекстовом (но не в графическом) режиме символы автоматически выводятся на экран и переходят с одной строки на другую. Для вывода на экран текста запроса или сообщения необходимо составить программу, которая устанавливает в регистре cx значение 01 и в цикле загружает в регистр al из памяти выводимые символы текста. Так как регистр cx в данном случае занят, то нельзя использовать команду loop. Кроме того, при выводе каждого символа необходимо дополнительно продвигать курсор в следующий столбец (функция 02).

Вграфическом режиме регистр bl используется для определения цвета графики. Если бит 7 равен 0, то заданный цвет заменяет текущий цвет точки; если бит 7 равен 1, то происходит комбинация цветов с помощью команды xor.

4.28. Расширенный ASCII-код

ASCII-коды от 128 до 255 (80h–FFh) представляют собой ряд специальных символов, полезных при формировании запросов, меню, специальных значков с экранными атрибутами.

4.29. Арифметические операции

Арифметические операции над целыми двоичными числами без знака рассмотрены в уч. пос., ч. 1, а над целыми двоичными числами со знаком – в уч. пос., ч. 3.

4.30. Преобразование типов для логических операций

Что делать, если размеры операндов, участвующих в логических операциях, разные? Например, предположим, что в операции ИЛИ один операнд является словом, а другой занимает двойное слово. Выше сказано, что в арифметической операции должны участвовать операнды одного формата. То же обязательно и для логических операций. Но в отличие от арифметических операций, в которых могут участвовать как числа беззнаковые, так и числа со знаком, в логических операциях операнды – беззнаковые. И выход – тот же, что и для беззнаковых операнд арифметических операций (см. уч. пос., ч. 1, п.4.69). В этом случае можно либо на базе исходного операнда сформировать новый (формата двойного слова), старшие разряды которого просто заполнить нулями, либо применить команду movzx, относящуюся к командам обработки строк (см.

уч. пос., ч. 7).

Примечание. Нельзя применять ни команды преобразования типов cbw, cwd, cwde, cdq (см. п.5.15), ни команду movsx, распространяющую знак на старшие позиции.

43

4.31. Команды работы со стеком

Эта группа команд (перемещение данных в стек и из стека) представляет собой набор специализированных команд, ориентированных на организацию гибкой и эффективной работы со стеком.

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

Для работы со стеком в МП 8086 предназначены три регистра:

ss - сегментный регистр стека;

sp - регистр указателя стека;

bp - регистр указателя базы кадра стека.

Размер стека зависит от режима работы микропроцессора и ограничивается 64 Кбайт (или 4 Гбайт в защищенном режиме).

В каждый момент времени доступен только один стек, адрес сегмента которого содержится в регистре ss. Этот стек называется текущим. Для того чтобы обратиться к другому стеку (“переключить стек”), необходимо загрузить в регистр ss другой адрес. Регистр ss автоматически используется процессором для выполнения всех команд, работающих со стеком.

Перечислим еще некоторые особенности работы со стеком:

запись и чтение данных в стеке осуществляется в соответствии с принципом lifo (Last In First Out - “последним пришел, первым ушел”);

по мере записи данных в стек последний растет в сторону младших адресов. Эта особенность заложена в алгоритм команд работы со стеком;

при использовании регистров sp и bp для адресации памяти ассемблер автоматически считает, что содержащиеся в нем значения представляют собой смещения относительно сегментного регистра ss.

В общем случае стек организован так, как показано на рис. 4.1.

Для работы со стеком предназначены регистры ss, sp и bp. Эти регистры используются комплексно, и каждый из них имеет свое функциональное назначение.

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

При занесении элемента в стек процессор уменьшает значение регистра

44

Рис. 4.1 Концептуальная схема организации стека

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

Если необходимо получить доступ к элементам не на вершине, а внутри стека, применяют регистр bp. Регистр bp - регистр указателя базы стека.

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

Начало стека расположено в старших адресах памяти. На рис. 4.1 этот адрес обозначен парой ss:ffff. Смещение ffff приведено здесь условно. Реально это значение определяется величиной, которую программист задает при описании сегмента стека в своей программе.

Адресная пара ss:ffff - это максимальное для реального режима значение адреса начала стека, так как размер сегмента в нем ограничен величиной

64 Кбайт (0ffffh).

Для организации работы со стеком существуют специальные команды записи и чтения.

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

включает следующие действия (рис. 4.2):

(sp) = (sp) – 2; значение sp уменьшается на 2;

45

значение из источника записывается по адресу, указываемому парой ss:sp.

Рис. 4.2. Принцип работы команды push

pop назначение - запись значения из вершины стека по месту, указанному операндом назначения. Значение при этом “снимается” с вершины стека.

Алгоритм работы команды pop обратен алгоритму команды push

(рис. 4.3):

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

(sp) = (sp) + 2; увеличение значения sp.

Рис. 4.3. Принцип работы команды pop

46

5. ТИПИЧНЫЕ ОШИБКИ ПРИ ВЫПОЛНЕНИИ РАБОТЫ

5.1.Перечень часто встречающихся ошибок

5.1.1.В перечень таких ошибок входят: те же, что и в первой лабораторной работе (см. уч. пос., ч. 1):

– открытая процедура или открытый сегмент;

– программист забывает о возврате в DOS;

– программист забывает о стеке или резервирует маленький стек;

– вызывает процедуру, которая портит содержимое нужных регистров;

– неопределенные символические имена;

– повторное определение символического имени;

– неправильный порядок операндов;

– неправильное использование операндов;

– потеря содержимого регистра при умножении;

– не подготовлены регистры при делении;

– неправильное использование регистра после деления;

– потеря содержимого регистра при делении;

– неправильное использование регистров при преобразовании байта в слово и слова в двойное слово в командах cbw и cwd;

– применение команд преобразования cbw и cwd к беззнаковым данным;

– изменение отдельными инструкциями флага переноса;

– программист долго не использует состояние флагов;

– ошибки при использовании регистров:

а) 8- и 16-разрядные регистры для записи двух операндов одной команды;

б) 8-разрядный регистр указывается для работы со стеком; в)сегментные регистры прямо используются в арифметических

вычислениях, логических операциях или для непосредственной передачи данных;

г) сегментные регистры могут использоваться либо как источники операндов, либо как приемники операндов, но никак не одновременно;

– выход из диапазона адресов.

5.1.2.Кроме того, входят следующие ошибки:

программист забывает об инструкции ret [4, с.100];

генерация неверного типа возврата [4, с.101];

ошибки при использовании условных переходов [4, с.106];

47

перепутаны операнды в памяти и непосредственные операнды [4,

с.117];

границы сегментов (4, с.118).

В каждом языке имеется свое множество ошибок, которые обычно очень легко сделать, но не всегда просто обнаружить. Не является исключением и язык ассемблера. Типичные ошибки, которые допускаются при программировании на ассемблере, и рекомендации, как можно их избежать, приведены ниже. Источники данной информации: [1], [4], [5], [6].

5.2. Открытая процедура или открытый сегмент [5, с.120–121]

Для каждой директивы proc должна быть записана соответствующая директива endp. Если директива endp пропущена или имя процедуры в записи endp записано неверно, ассемблер генерирует предупреждающее сообщение, что процедура не закрыта надлежащим образом. Точно так же директива ends должна быть задана для каждой директивы segment. Если директива ends пропущена или имя сегмента в ней указано неверно, генерируется предупреждающее сообщение, что сегмент открыт. Наиболее вероятной причиной ошибки является неверная запись имени сегмента или процедуры.

5.3. Программист забывает о возврате в DOS

Имеется несколько вариантов возврата в DOS. Правильно завершать работу будет, например, программа, использующая команду ret или функцию 4Ch (см. уч. пос., ч. 1).

5.4. Программист забывает о стеке или резервирует маленький стек

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

(см. уч. пос., ч. 3).

Обычно выделяют стек размером 512 байтов.

Не требуется выделять стек в программах, которые предполагается преобразовать в файлы типа .com (см. уч. пос., ч. 3).

48

5.5. Вызов подпрограммы, которая портит содержимое нужных регистров

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

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

5.6. Неопределенные символические имена [5, с.121–122]

Неопределенные символические имена представляют собой тип ошибок, которые наиболее часто встречаются при программировании. ассемблер генерирует сообщение о такой ошибке, если имя не определено в поле метки какой-либо строки ассемблерной программы. Если имя определено в другой программе, то его следует определить как внешнее имя директивой extrn (external - внешний). Неправильная запись имени является наиболее вероятной причиной такой ошибки, особенно в тех случаях, когда используемые шестнадцатеричные константы записываются без начальной цифры (см. уч. пос., ч. 1).

5.7. Повторное определение символического имени [5, с.122–123]

Ассемблер формирует сообщение об ошибке повторного определения символического имени в тех случаях, когда одно и то же имя или метка два и более раз повторяются в поле метки ассемблерных строк программы (см. уч. пос., ч. 1).

5.8. Неправильный порядок операндов

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

mov ax,bx

рассматривать, как ax := bx

49

5.9.Неправильное использование операндов

Вязыке ассемблера первый операнд всегда используется в качестве приемника (кроме синтаксиса АТ&T для UNIX и Linux, см. уч. пос., ч. 12). Операнд может быть записан в регистре общего назначения, регистре сегмента или ячейке памяти. При использовании непосредственных данных в качестве операнда-приемника

cmp З,al ; неправильная запись cmp al,3 ; правильная запись генерируется сообщение об ошибке.

5.10.Потеря содержимого регистра при умножении

Всинтаксисе инструкций mul и imul явно указываются только один из операндов и размер, а регистры, используемые в качестве операндаприемника, просто подразумеваются. Это приводит к тому, что легко можно упустить из виду использование какого-либо неявного регистра. Например, программист знает, что результат перемножения 16разрядного значения на 16-разрядную величину поместится в регистр ax,

ине учитывает, что содержимое регистра dx теряется Поэтому всегда нужно помнить о том, что при использовании инструкций mul и imul изменяется (не сохраняется) содержимое не только регистров al, ax или eax, но также и ah, dx или edx.

5.11. Не подготовлены регистры при делении

Распространенной ошибкой для случаев, когда делимое по размеру совпадает с делителем и размещено в al, ax или eax, является то, что программист не обнуляет регистры ah, dx, edx (см. уч. пос., ч. 1).

5.12. Неправильное использование регистра после деления

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

Пример ошибочного использования регистра edx после деления учетверенного слова на двойное слово приведен в уч. пос., ч. 7.

5.13. Потеря содержимого регистра при делении

Потеря содержимого регистра при делении заключается в том, что теряется значение, помещенное в регистр до операции деления (значение регистров al, ah , ax, dx, eax, edx, помещенное в регистр до операции деления, теряется, т.к. в регистр будет помещено частное (в регистры al, ax, eax) или остаток от деления (в регистры ah , dx, edx), см. уч. пос., ч. 1 и 7.

50

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]