Работа с расширенной памятью
После резервирования блока и отображения части или всех
его логических страниц с расширенной памятью могут работать все без исключения
команды микропроцессора. В данном разделе описан пример пересылки большого
массива данных и обсуждается возможность одновременного использования
двух блоков, расположенных в расширенной памяти.
Способ пересылки большого блока
При выполнении графических задач может потребоваться
сохранение содержимого всего рабочего пространства видеопамяти. Учитывая
его большие размеры, сохранение в обычной памяти либо не целесообразно,
либо просто невозможно, для этого лучше подходит расширенная память.
Размер рабочего пространства видеопамяти зависит от
установленного видеорежима, он вычисляется умножением размера строки в
байтах на количество строк на экране (bperiine versize). Для временного
хранения произведения нужна переменная, состоящая из двух слов (но не
одно двойное слово), назовем ее bisize. В первом слове будет храниться
старшая часть произведения, а во втором слове — младшая. Старшая часть
указывает количество полных окон видеопамяти, а младшая часть — количество
байтов в последнем окне, оно может быть равно нулю.
Если в результате умножения в первом слове bisize окажется
число N, а во втором bisize+2 число м, то размер отображаемой части видеопамяти
составляет N* 6553б + м байтов, т. е. надо переслать N полных сегментов
и еше м байтов. Поэтому перед пересылкой очередного фрагмента данных необходимо
уточнять его размер.
Кроме того, перед пересылкой каждого фрагмента производится
отображение очередной группы логических страниц блока, выделенного в расширенной
памяти на физические с помощью подпрограммы mapseg, приведенной в примере
Б.6 Для исключения лишних проверок можно каждый раз отображать по 4 страницы.
В процессе отображения mapseg изменяет номера логических страниц (содержимое
регистра bx), поэтому при работе с ней достаточно задать номер исходной
страницы и в дальнейшем просто не изменять текущее содержимое регистра
bx.
Для упрощения и ускорения пересылки нужен микропрограммный
цикл на основе строковой операции movs, работающей с двойными словами.
Подпрограмма пересыки блока
Программная реализация пересылки показана в примере
Б.7. Перед вызовом подпрограммы в регистрах es и fs указываются коды видеобуфера
и сегмента EMS, в примере Б.8 показано, как это делается.
Предполагается, что в разделе данных задачи описана переменная
bisize, а в расширенной памяти зарезервирован блок, размер которого не
меньше размера отображаемой части видеопамяти.
Пример Б.7. Пересылка содержимого рабочей области экрана
movebl: push Cur_win pusha
mov ax, BaseWin
mov Cur_win, ax
call Setwin
mov ax, bperline
mul versize
mov bisize, dx
mov blsize+2, ax
xor bx, bx
mov dx, Ehndlr
xor di, di
xor si, si
сохраняем текущее окно видеопамяти
сохраняем "все" регистры
ах = BaseWin (или ах = 0)
Cur_win = BaseWin
установка нулевого окна видеопамяти
ах = размер строки в байтах
dx:ax = bperline * versize
bisize = число полных окон
bisize +2 = размер последнего окна
bx = исходная логическая страница
dx = идентификатор файла
di = 0 исходный адрес
si = 0 исходный адрес
mloop: mov ex, 4000h 0,25 размера стандартного сегмента
dec blsize уменьшаем количество сегментов
jns sc I -> пересылка полного окна
mov ex, blsize+2 ex = размер последнего окна
shr ex, 02 уменьшаем его в 4 раза
je sc__2 -> окно пустое, пересылка окончена
sc I: call mapseg отображаем очередные 4 страницы
rep movs dword ptr [di], fs:[si]; цикл пересылки
call Nxtwin следующее окно видеопамяти
cmp blsize, -1 пересылка завершена ?
jne mloop -> нет, продолжаем пересылку
sc_2: pop Cur_win исходное значение видеоокна
рора восстановление "всех" регистров
call Setwin восстановление исходного окна
ret возврат из подпрограммы
Выполнение примера Б. 7 начинается с сохранения исходного
окна видеопамяти, содержимого всех регистров и установки базового окна.
Если переменная Basewin в задаче не используется, то надо просто установить
нулевое окно. Затем вычисляется размер отображаемой области видеопамяти,
и результат сохраняется в словах bisize и bisize+2. В регистр bx помещается
номер нулевой логической странице, а в dx — идентификатор блока. Содержимое
этих двух регистров использует только подпрограмма mapseg. Подготовка
оканчивается очисткой содержимого индексных регистров si и di.
Основной цикл имеет метку mloop. Его первые шесть команд
определяют размер фрагмента пересылаемых данных. Он составляет 16 384
двойных слова, если окно заполнено полностью, или равен значению слова
bisize+2, уменьшенному в 4 раза, если окно заполнено частично.
Команда, имеющая метку sc_i, отображает очередные четыре
страницы блока на сегмент EMS. Затем микропрограммный цикл пересылает
очередной фрагмент данных. Он выполняет основную работу, все остальные
команды примера Б. 7 являются вспомогательными.
После пересылки очередного фрагмента проверяется содержимое
bisize, и работа подпрограммы продолжается до тех пор, пока его значение
не окажется равным "-1". В этом случае из стека выталкиваются
значения переменной Cur_win и сохраненных регистров, восстанавливается
исходное окно видеопамяти и происходит возврат на вызывающий модуль.
Сохранение и восстановление рабочей области экрана
В примере Б. 7 основные действия выполняет строковая
операция movs, у которой расположение источника задает регистр fs, а приемника
— es. Следовательно, для сохранения содержимого видеопамяти в расширенной
памяти в регистр fs надо записать код видеосегмента, а в es — код сегмента
EMS. Для восстановления содержимого видеопамяти, сохраненного в расширенной
памяти в регистре fs, указывается код сегмента EMS, а в es — код видеобуфера.
Формирование нужных значений в регистрах es и fs выполняют подпрограммы,
приведенные в примере Б.8. Для сохранения содержимого видеопамяти используется
обращение к подпрограмме scrsave, а для восстановления — к scrrest. Входные
параметры отсутствуют.
Пример Б.8. Сохранение или восстановление рабочей
области экрана
scrsave: PushReg <fs,es,vbuff,ebuff>; размещение
в стеке
jmp short @F обход макровызова
scrrest: PushReg <fs,es,ebuff vbuff>; размещение в стеке
@@: call Hidepnt удаление изображения курсора
PopReg <es,fs> формируем содержимое es и fs
call Movebl перемещение блока
PopReg <es,fs> восстановление содержимого es и fs
call Showpnt вывод курсора на экран
ret возврат из подпрограммы
При вызове scrsave в стеке сохраняется исходное содержимое
регистров fs, es и переменных vbuff, ebuff. При обращении к scrrest порядок
записи в стек переменных ebuff, vbuff противоположный. После размещения
в стеке нужных величин выполняется общая часть обеих подпрограмм.
Прежде всего надо удалить изображение курсора с экрана,
иначе при сохранении оно станет частью общей картины, а при восстановлении
на экране могут появиться два курсора, или при перемещении на месте курсора
окажется прямоугольник другого цвета.
После этого в регистры fs и es выталкиваются из стека
нужные величины, происходит обращение к подпрограмме movebi и восстанавливается
исходное содержимое регистров fs и es.
Выполнение подпрограмм заканчивается восстановлением
изображения курсора на экране. Напомним, что варианты подпрофамм Hidepnt
и Showpnt описаны в главе 6.
Несколько блоков в расширенной памяти
При работе с обычной памятью каждому блоку соответствует
свой уникальный код сегмента. В отличие от обычной, при работе с расширенной
памятью доступ ко всем зарезервированным задачей блокам осуществляется
через один и тот же сегмент EMS. В таком случае по коду сегмента невозможно
определить блок, к которому происходит обращение. Для этой цели можно
использовать только смещение (адрес), по которому выбираются или записываются
данные. Для того чтобы понять, к чему это приводит, рассмотрим простой
вариант работы с двумя блоками.
Предположим, что доступны два блока, и надо переписать
данные из одного в другой. Если один из блоков расположен в видео или
в обычной памяти, а другой в расширенной, то размер пересылаемой порции
данных ограничен адресным пространством сегмента, т. е. величиной 65 536
байтов. В этом вы могли убедиться на примере Б.7.
Если же оба блока расположены в расширенной памяти,
то пространство сегмента EMS придется разделить пополам и использовать
младшие адреса для работы с одним из блоков, а старшие — с другим. В результате
этого в каждом блоке будет доступно пространство размером 32 768 байтов.
Никаких других ограничений нет.
В таком случае перед копированием порции данных придется
дважды обратиться к подпрограмме отображения страниц, описанной в примере
Б.6, через точку входа mapip. Сначала отображаются две очередные логические
страницы блока 1 на физические страницы 0 и 1, затем две очередные логические
страницы блока 2 на физические страницы 2 и 3. Блок 1 начинается с нулевого
адреса сегмента EMS, а блок 2 с адреса soooh того же сегмента. Предельный
размер доступного пространства в обоих блоках составляет soooh или 32
768 байтов. После этого можно использовать любые команды для работы с
отображенным пространством, например, строковую операцию movs, выполняющую
перемещение данных из блока в блок.
Если особенности алгоритма требуют одновременной работы
с тремя или четырьмя блоками, то доступное пространство будет ограничено
размером одной страницы, т. е. величиной 16 384 байта.
Здесь уместно отметить, что в набор Advanced Functions
драйвера EMM включена специальная функция, предназначенная для перемещения
или обмена содержимого (перестановки) двух блоков данных размером до 1
Мбайт, ее код 57h. Блоки могут располагаться в обычной или расширенной
памяти. Перед вызовом функции 57h в регистре ai указывается 0 для пересылки
или 1 для перестановки блоков. Кроме того, в регистрах ds:si указывается
адрес начала специальной структуры данных, содержащей размер блока и данные
об источнике и приемнике. Описание этой функции вы можете найти, например,
в Tech Help, нам важно было напомнить о ее существовании.
Заключение.
Мы закончили описание основных видов оперативной памяти, поэтому можно
подвести общий итог. При работе в среде DOS для программ доступна как
основная, так и дополнительная память. Реальные размеры последней существенно
больше размеров первой, поэтому при разработке задач следует отдавать
предпочтение расположению больших блоков в расширенной памяти, а блоки
небольшого размера размещать в обычной памяти. Кроме того, следует избегать
одновременного использования нескольких блоков, расположенных в расширенной
памяти, т. к. это связано с ограничением доступного пространства адресов.
|