Жанр: Учеба

Ассемблер и программирование для IBM PC

сторінка №32

ентной программы, которая хотя и присоединена к DOS, но не является
внутренней программой DOS, как DIR, COPY или CLS. Общим подходом является
модификация таблицы векторов прерываний таким образом, чтобы резидентная
программа, получала управление при нажатии определенных клавиш или их
комбинаций, а все остальные передавала через себя. Резидентная программа
обычно (но не обязательно) состоит из следующих частей:

1) секции, переопределяющей адреса в таблице векторов
прерываний;
2) процедуры, выполняющейся только один раз при загрузке
программы и предназначенной для:

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

3) процедуры, которая остается резидентной и активизируется,
например, по вводу с клавиатуры или по сигналам таймера.

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

Таблица векторов прерываний
IВМВIO.СОМ и IBMDOS.COM
COMMAND.СОМ
Резидентная часть программы - остается в памяти
Инициализирующая часть программы - перекрывается следующей
программой
Остальная часть доступной памяти

__________________________________________________________________________

TITLE RESIDENT (COM) Резидентная программа для очистки
; экрана и установки цвета при нажатии
; Alt+Left Shift
;----------------------------------------------------------
INTTAB SEGMENT AT 0H ;Таблица векторов прерываний:
ORG 9H*4 ; адрес для Int 9H,
KBADDR LABEL DWORD ; двойное слово
INTTAB ENDS
;----------------------------------------------------------
ROMAREA SEGMENT AT 400H ;Область параметров BIOS:
ORG 17H ; адрес флага клавиатуры,
KBFLAG DB ? ; состояние Alt + Shift
ROMAREA ENDS
;----------------------------------------------------------
CSEG SEGMENT PARA ;Сегмент кода
ASSUME CS:CS
ORG 100H
BEGIN: JMP INITZ ;Выполняется только один раз
KBSAVE DD ? ;Для адреса INT 9 BIOS
; Очистка экрана и установка цветов:
; ---------------------------------
COLORS PROC NEAR ;Процедура выполняется
PUSH AX ; при нажатии Alt+Left Shift
PUSH BX
PUSH CX ;Сохранить регистры
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
PUSHF
CALL KBSAV ;Обработать прерывание
ASSUME DS:ROMAREA
MOV AX,ROMAREA ;Установить DS для
MOV DS,AX ; доступа к состоянию
MOV AL,KB AG ; Alt+Left Shift
CMP AL,00001010B ;Alt+Left Shift нажаты?
JNE EXIT ; нет - выйти
MOV AX,0600H ;Функция прокрутки
MOV BH,61H ;Установить цвет
MOV CX,00
MOV DX,18 FH
INT 10H
EXIT:
POP ES ;Восстановить регистры
POP DS
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
IRET ;Вернуться
COLORS ENDP
; Подпрограмма инициализации:
; --------------------------
INITZE PROC NEAR ;Выполнять только один раз
ASSUME DS:INTTAB
PUSH DS ;Обеспечить возврат в DOS
MOV AX,INTTAB ;Установить сегмент данных
MOV DS,AX
CLI ;Запретить прерывания
;Замена адреса обработчика:
MOV AX,WORD PTR KBADDR ;Сохранить адрес
MOV WORD PTR KBSAVE,AX ; BIOS
MOV AX,WORD PTR BADDR+2
MOV WORD PTR KBSAVE+2,AX
MOV WORD PTR KBADDR,OFFSET COLORS ;Заменить
MOV WORD PTR KBADDR+2,CS ; адрес BIOS
STI ;Разрешить прерывания
MOV DX,OFFSET INITZE ;Размер программы
INT 27H ;Завершить и остаться
INITZE ENDP ; резидентом

CSEG ENDS
END BEGIN
__________________________________________________________________________

Рис.23.2 Резидентная программа

Пример на рис.23.2 иллюстрирует резидентную программу, которая
устанавливает цвет экрана при одновременном нажатии клавиш Alt и Left
Shift. Основные моменты, представляющие интерес:
Сегмент INTTAB определяет таблицу векторов прерываний, начинающуюся
по адресу 0, а точнее - адрес элемента для прерывания 9 (ввод с
клавиатуры), названный в программе KBADDR.
Имя ROMAREA определяет сегмент, начинающийся по адресу 400Н, и в нем
флаговый байт клавиатуры (KBFLAG), который отражает состояние клавиатуры.
Бит 3 в этом байте регистрирует нажатие клавиши Alt, а бит 1 - нажатие
клавиши Left Shift.
Сегмент CSEG начинает сегмент обычной COM-программы. Первая
выполняемая команда JMP INITZE обходит резидентную часть и передает
управление в процедуру инициализации (INITZE) в конце программы. Эта
процедура устанавливает в регистре DS адрес таблицы векторов прерывания
(INTTAB) и передает адрес элемента таблицы для INT 9 (KBADDR) в поле
KBSAVE в резидентной процедуре. Следующим шагом в таблице в KBADDR
утанавливается адрес резидентной процедуры (COLORS) (первое слово) и
содержимое регистра CS (второе слово). Таким образом, KBADDR содержит
теперь два измененных слова: смещение и значение адреса из регистра CS,
которые вместе определяют адрес процедуры COLORS в памяти, куда будут
направляться теперь все символы, поступающие с клавиатуры. Затем процедура
инициализации заносит в регистр DX размер процедуры COLORS (адрес INITZE
на один байт больше, чем адрес конца процедуры COLORS) и прекращает
работу, используя INT 27H.
Процедура COLORS является резидентной, и она получает управление при
нажатии любой клавиши на клавиатуре. Так как это происходит при работе
других программ (например, DOS или текстового редактора), то процедура
должна сохранить все регистры, которые она использует (а также несколько
других на всякий случай). Затем происходит вызов по адресу KBSAVE, т.е.
вызов подпрограммы обработки прерывания, после чего процедура проверяет
флаг клавиатуры для определения нажатия клавиш Alt и Left Shift. Если эти
клавиши были нажаты, то процедура устанавливает необходимые цвета.
Завершающие команды включают восстановление всех запомненных вначале
регистров (в обратной последовательности) и выход из обработки прерывания
по команде IRET.
Поскольку приведенная программа носит иллюстративный характер, ее
можно модифицировать или расширить для собственных целей. Некоторые
фирменные программы, также изменяющие адрес в векторной таблице для
прерывания 9, не разрешают конкурентное использование резидентных
программ, аналогичных рассмотренной в данной главе.

ПОРТЫ
________________________________________________________________

Порт представляет собой устройство, которое соединяет процессор с
внешним миром. Через порт процессор получает сигналы с устройств ввода и
посылает сигналы на устройство вывода. Теоретически процессор может
управлять до 65 536 портами, начиная с нулевого порта. Для управления
вводом-выводом непосредственно на уровне порта используются команды IN и
OUT:
- Команда IN передает данные из входного порта в регистр AL
(байт) или в регистр АХ (слово). Формат команды:

IN регистр,порт

- Команда OUT передает данные в порт из регистра AL (байт) или
из регистра АХ (слово). Формат команды:

OUT порт,регистр

Номер порта можно указывать статически или динамически:

1. Статическое указание порта возможно при непосредственном
использовании значения от 0 до 255:

Ввод: IN AL.порт# ;Ввод одного байта
Вывод: OUT порт#,АХ ;Вывод одного слова

2. Динамическое указание порта устанавливается в регистре DX от
0 до 65535. Этот метод удобен для последовательной обработки
нескольких портов. Значение в регистре DX в этом случае увеличивается
в цикле на 1. Пример ввода байта из порта 60Н:

MOV DX,60H ;Порт 60Н (клавиатура)
IN AL,DX ;Ввод байта

Ниже приведен список некоторых портов (номера в шестнадцатиричном
представлении):

21 Регистры маски прерывании.
40...42 Таймер/счетчик
60 Ввод с клавиатуры
61 Звуковой порт (биты 0 и 1)
201 Управление играми
3B0...3BF Монохромный дисплей и параллельный адаптер печати
3D0...3DF Цветной/графический адаптер
3F0...3F7 Дисковый контроллер

Если, например, программа запрашивает ввод с клавиатуры, то она
выдает команду прерывания INT 16H. В этом случае система устанавливает
связь с BIOS, которая с помощью команды IN вводит байт с порта 60Н.
На практике рекомендуется пользоваться прерываниями DOS и BIOS.
Однако можно также успешно обойтись без BIOS при работе с портами 21,
40...42, 60 и 201. Листинги BIOS в техническом руководстве по IBM PC
содержат различные примеры команд IN и OUT.

ГЕНЕРАЦИЯ ЗВУКА
________________________________________________________________

Компьютер имеет возможность генерировать звук посредством встроенного
динамика с постоянным магнитом. Можно выбрать один из двух способов
управления динамиком или использовать оба в комбинации: 1) использование
бита 1 порта 21 Н для активизации микросхемы Intel 8255A-5
(программируемый периферийный интерфейс) (РР1); 2) использование триггера
программируемого интервального таймера Intel 8353-5 (PIT). Часовой
генератор выдает сигнал с частотой 1,19318 МГц. Таймер PPI управляет
триггером 2 через бит 0 порта 61 Н.
Программа на рис.23.3 генерирует серию звуковых нот с возрастающей
частотой. Значение в поле DURTION формирует продолжительность звучания
каждой ноты, а значение в поле TONE определяет частоту звучания. В начале
работы программа считывает содержимое порта 61 Н и сохраняет полученное
значение. Команда CLI сбрасывает флаг прерываний, обеспечивая равномерное
звучание. Интервальный таймер генерирует 18,2 такта в секунду, что (при
отсутствии CLI) прерывает выполнение программы и вызывает появление звука.
Значение поля TONE определяет частоту звука: большие значения дают
низкую частоту, а малые - высокую. После того, как подпрограмма BIOSPKR
исполнит очередную ноту, она увеличивает частоту звука посредством сдвига
вправо на 1 бит значения в поле TONE (т.е. делит значение поля TONE на 2).
Так как уменьшение TONE в данном примере приводит к сокращению
продолжительности звучания, то подпрограмма также увеличивает DURTION
посредством сдвига его значения на один бит влево, т.е. удваивает это
значение.
Программа завершается, когда содержимое поля TONE уменьшится до нуля.
Начальные значения в полях DURTION и TONE не имеют принципиального
значения. Можно поэкспериментировать с другими значениями, а также
испытать выполнение без команды CLI. Можно изменить программу для
генерации звуков с уменьшающейся частотой, установив для этого в поле TONE
значение 01, а в поле DURTION - некоторое большее значение. В каждом цикле
при этом необходимо увеличивать значение в поле TONE и уменьшать значение
в поле. DURTION. При достижении нуля в DURTION программу можно завершить.
Можно использовать любые варианты исполнения последовательности нот,
например, для привлечения внимания пользователя.

__________________________________________________________________________

TITLE SOUND (COM) Процедура для генерации звука
SOUNSG SEGMENT PARA 'Code'
ASSUME CS:SOUNG,DS:SOUNG,SS:SOUNG
ORG 100H
BEGIN: JMP SHORT MAIN
; -------------------------------------------------------
DURTION DW 1000 ;Время звучания
TONE DW 256H ;Высота (частота) звука
; -------------------------------------------------------
MAIN PROC NEAR
IN AL,61H ;Получить и сохранить
PUSH AX ; данные порта
CLI ;Запретить прерывания
CALL B10SPKR ;Произвести звук
POP AX ;Восстановить значение
OUT 61H,AL ; порта
STI ;Разрешить прерывания
RET
MAIN ENDP

B10SPKR PROC NEAR
B20: MOV DX,DURTION ;Установить время звучания
B30:
AND AL,11111100B ;Очистить биты 0 и 1
OUT 61H,AL ;Передать на динамик
MOV CX,TONE ;Установить частоту
B40:
LOOP B40 ;Задержка времени
OR AL,00000010B ;Установить бит 1
OUT 61H,AL ;Передать на динамик
MOV CX,TONE ;становить частоту
B50:
LOOP B50 ;Задержка времени
DEC DX ;Уменьшить время звучания
JNZ B30 ;Продолжать?
SHL DURTION,1 ; нет - увеличить время,
SHR TONE,1 ; сократить частоту
JNZ B20 ;Нулевая частота?
RET ; да - выйти
B10SPKR ENDP

SOUNSG ENDS
END BEGIN
__________________________________________________________________________

Рис.23.3 Генерация звука

ГЛАВА 24 Справочник по директивам языка Ассемблер


__________________________________________________________________________

Ц е л ь: подробно описать операторы и директивы языка Ассемблер.

ВВЕДЕНИЕ
________________________________________________________________

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

ИНДЕКСНАЯ АДРЕСАЦИЯ ПАМЯТИ
________________________________________________________________

При прямой адресации памяти в одном из операндов команды указывается
имя определенной переменной, например для переменной COUNTER:

ADD CX,COUNTER

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

- базовый регистр BX в виде [BX] вместе с сегментным регистром
DS или базовый регистр BP в виде [BP] вместе с сегментным регистром
SS. Например, с помощью команды

MOV DX,[BX] ;Базовый регистр

в регистр DX пересылается элемент, взятый по относительному адресу в
регистре BX и абсолютному адресу сегмента в регистре DS;
- индексный регистр DI в виде [DI] или индексный регистр SI в
виде [SI], оба вместе с сегментным регистром DS. Например, с помощью
команды

MOV AX,[SI] ;Индексный регистр

в регистр АХ пересылается элемент, взятый по относительному адресу в
регистре SI и абсолютному адресу сегмента в регистре DS;
- [константу], содержащую непосредственный номер или имя в
квадратных скобках. Например, с помощью команды

MOV [BX+SI+4],AX ;База+индекс+константа

содержимое регистра АХ пересылается по адресу, который вычисляется,
как сумма абсолютного адреса в регистре DS, относительного адреса в
регистре BX, относительного адреса в регистре SI и константы 4;
- смещение (+ или -) совместно с индексным операндом. Существует
небольшое различие при использовании константы и смещения. Например,
с помощью команды

MOV DX,8[DI][4] ;Смещение+индекс+константа

в регистр DX пересылается элемент, взятый по абсолютному адресу в
регистре DS, смещению 8, относительному адресу в регистре DI и
константе 4.

Эти операнды можно комбинировать в любой последовательности. Но
нельзя использовать одновременно два базовых регистра [BX + BP] или два
индексных регистра [DI + SI]. Обычно индексированные адреса используются
для локализации элементов данных в таблицах.

ОПЕРАТОРЫ ЯЗЫКА АССЕМБЛЕР
________________________________________________________________

Существует три типа ассемблерных операторов: операторы атрибута,
операторы, возвращающие значение, и операторы. специфицирующие битовую
строку. В данном разделе рассмотрены первые два типа операторов.
Операторы, специфицирующие битовую строку, оператор MASK, счетчик сдвига и
оператор WIDTH относятся к директиве RECORD и будет рассмотрены в
следующем разделе.

Оператор LENGTH
-----------------
Оператор LENGTH возвращает число элементов, определенных операндом
DUP. Например, следующая команда MOV заносит в регистр DX значение 10:

TABLEA DW 10 DUP(?)
MOV DX,LENGTH TABLEA

Если операнд DUP отсутствует, то оператор LENGTH возвращает значение
01. См. операторы SIZE и TYPE в этом разделе.

Оператор OFFSET
------------------
Оператор OFFSET возвращает относительный адрес переменной или метки
внутри сегмента данных или кода. Оператор имеет следующий формат:

OFFSET переменная или метка

Например, команда

MOV DX,OFFSET TABLEA

устанавливает в регистре DX относительный адрес (смещение) поля TABLEA в
сегменте данных. (Заметим, что команда LEA выполняет аналогичное действие,
но без использования оператора OFFSET.)

Оператор PTR
---------------
Оператор PTR используется совместно с атрибутами типа BYTE, WORD или
DWORD для локальной отмены определенных типов (DB, DW или DD) или с
атрибутами NEAR или FAR для отмены значения дистанции по умолчанию. Формат
оператора следующий:

тип PTR выражение

В поле "тип" указывается новый атрибут, например BYTE. Выражение имеет
ссылку на переменную или константу. Приведем несколько примеров оператора
PTR:

FLDB DB 22H
DB 35H
FLDW DW 2672H ;0бьектный код 7226
MOV АН,BYTE PTR FLDW ;Пересылает 1-й байт (72)
ADD BL,BYTE PTR FLDW+1 ;Прибавляет 2-й байт (26)
MOV BYTE PTR FLDW,05 ;Пересылает 05 в 1-й байт
MOV AX,WORD PTR FLDB ;3аносит в АХ байты (2235)
CALL FAR PTR[BX] ;Длинный вызов процедуры

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

Оператор SEG
--------------
Оператор SEG возвращает адрес сегмента, в котором расположена
указанная переменная или метка. Наиболее подходящим является использование
этого оператора в программах, состоящих из нескольких отдельно
ассемблируемых сегментов. Формат оператора:

SEG переменная или метка

Примеры применения оператора SEG в командах MOV:

MOV DX,SEG FLOW ;Адрес сегмента данных
MOV DX,SEG A20 ;Адрес сегмента кода

Оператор SHORT
----------------
Назначение оператора SHORT - модификация атрибута NEAR в команде JMP,
если переход не превышает границы +127 и -128 байт:

JMP SHORT метка

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

Оператор SIZE
---------------
Оператор SIZE возвращает произведение длины LENGTH и типа TYPE и
полезен только при ссылках на переменную с операндом DUP. Формат
оператора:

SIZE переменная

См. пример для оператора TYPE.

Оператор TYPE
---------------
Оператор TYPE возвращает число байтов, соответствующее определению
указанной переменной:

Определение Число байтов

DB 1
DW 2
DD 4
DQ 8
DT 10
STRUC Число байтов, определённых в STRUC
NEAR метка FFFF
FAR метка FFFE

Формат оператора TYPE:

TYPE переменная или метка

Ниже приведены примеры, иллюстрирующие применение операторов TYPE, LENGTH
и SIZE:

FLDB DB ?
TABLEA DW 20 DUP(?) ;Определение 20 слов
MOV AX,TYPE FLDB ;AX = 0001
MOV AX,TYPE TABLEA ;AX = 0002
MOV CX,LENGTH TABLEA ;CX = 000A (10)
MOV DX,SIZE TABLEA ;DX = 0014 (20)

Так как область TABLEA определена как DW, то оператор TYPE возвращает
0002Н, LENGTH - 000АН (соответственно операнду DUP) и SIZE - произведение
типа и длины, т.е. 14Н (20).

ДИРЕКТИВЫ АССЕМБЛЕРА
________________________________________________________________

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

Директива ASSUME
------------------
Назначение директивы ASSUME - установить для ассемблера связь между
сегментами и сегментными регистрами CS, DS, ES и SS. Формат директивы:

ASSUME сегментный_регистр:имя [, ... ]

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

ASSUME CS:CODESG,DS:DATASG,SS:STACK,ES:DATASG

Для отмены любого ранее назначенного в директиве ASSUME сегментного
регистра необходимо использовать ключевое слово NOTHING:

ASSUME ES:NOTHING

Если, например, регистр DS оказался не назначен или отменен ключевым
словом NOTHING, то для ссылки к элементу из сегмента данных в командах
используется операнд со ссылкой к регистру DS:

MOV AX,DS:[BX] ;Использование индексного адреса
MOV AX,DS:FLDW ;Пересылка содержимого поля FLDW

Конечно, регистр DS должен содержать правильное значение сегментного
адреса.

Директива EXTRN
-----------------
Назначение директивы EXTRN - информировать ассемблер о переменных и
метках, которые определены в других модулях, но имеют ссылки из данного
модуля. Формат директивы:

EXTRN имя: тип [, ... ]

Директива EXTRN подробно рассмотрена в гл.21.

Директива GROUP
-----------------
Программа может содержать несколько сегментов одного типа (код,
данные, стек). Назначение директивы GROUP - собрать однотипные сегменты
под одно имя так, чтобы они поместились в один сегмент объемом 64 Кбайт,
формат директивы:

имя GROUP имя сегмента [, ... ]

Следующая директива GROUP объединяет SEG1 и SEG2 в одном ассемблерном
модуле:

GROUPX GROUP SEG1,SEG2
SEG1 SEGMENT PARA 'CODE'
ASSUME CS:GROUPX
SEG1 ENDS
SEG2 SEGMENT PARA 'CODE'
ASSUME CS:GROUPX
SEG2 ENDS

Директива INCLUDE
-------------------
Отдельные фрагменты ассемблерного кода или макрокоманды могут
использоваться в различных программах. Для этого такие фрагменты и
макрокоманды записываются в отдельные дисковые файлы, доступные для
использования из любых программ. Пусть некоторая подпрограмма,
преобразующая ASCII-код в двоичное представление, записана на диске С в
файле по имени CONVERT.LIB. Для доступа к этому файлу необходимо указать
директиву

INCLUDE C:CONVERT.LIB

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

(Если файл не будет найден, то ассемблер выдаст соответствующее сообщение
об ошибке и директива INCLUDE будет игнорирована.)
Для каждой вставленной строки ассемблер выводит в LST-файл в 30-й
колонке символ С (исходный текст в LST-файле начинается с 33-й колонки).
В гл.20 (Макрокоманды) дан практический пример директивы INCLUDE и
дано объяснение, каким образом можно использовать эту директиву только в
первом проходе ассемблера.

Директива LABEL
-----------------
Директива LABEL позволяет переопределять атрибут определенного имени.
Формат директивы:

имя LABEL тип

В качестве типа можно использовать BYTE, WORD или DWORD для
переопределения областей данных или имен структур или битовых строк.
Директивой LABEL можно переопределить выполнимый код, как NEAR или FAR.
Эта директива позволяет, например, определить некоторое поле и как DB, и
как DW. Ниже проиллюстрировано использование типов BYTE и WORD:

REDEFB LABEL BYTE
FIELDW DW

Список сторінок

Поділитися у соцмережах

☏ Замовлення реклами: +3805044-68-872

© Асоціація електронних бібліотек України

☝ Усі матеріали сайту (зокрема статті, зображення, рекламні оголошення тощо) призначені лише для попереднього ознайомлення. Усі права на публікації, представлені на сайті, належать їхнім законним власникам. Просимо Вас не зберігати копії інформації.