|
| |||||||||||||||||||||||
Паскаль для новичков (часть 26)Спрашивали? Отвечаю…640К для Паскаля не пределАвтор: Владислав Демьянишин
![]() Сегодня я расскажу об extended-памяти, которая впервые появилась в компьютерах на базе процессора INTEL80286. В компьютерах на базе процессоров INTEL80386 и выше всегда есть extended-память (eXtended Memory Specification – спецификация дополнительной памяти XMS, это вся оперативная память свыше границы первого мегабайта) и обычно нет аппаратной expanded-памяти (EMS), хотя её можно эмулировать с помощью драйвера EMM386, QEMM и т.п.
Существует всем известный XMS-драйвер HIMEM.SYS, обеспечивающий работу программ, использующих extended-память. Как настраивать работу этого драйвера через файл CONFIG.SYS, думаю, объяснять не нужно.
В данной статье я расскажу как составить модуль, назовём его, XMS.PAS, который бы содержал все необходимые функции для выделения больших непрерывных блоков памяти, размер которых ограничен лишь 64Мб или всей extended-памятью, если её размер меньше 64Мб. Предположим, что у нас в машине 128Мб ОЗУ, тогда при работе под MS-DOS можно будет использовать не более 64Мб XMS. При работе в сеансе MS-DOS под Windows можно будет использовать преимущество виртуальной памяти и запрашивать блоки XMS суммарным объёмом гораздо больше 128Мб.
В моём модуле XMS описано много полезных функций для работы с большими блоками extended-памяти (EMB – Extended Memory Block), но в данной статье я постараюсь лаконично осветить лишь самые необходимые.
Для начала следует установить директиву компиляции {$G+}, которая включает генерацию машинных инструкций для процессора INTEL80286. Затем опишем структуру TLinePtr для хранения 32-битного адреса (во всех возможных вариациях) и комбинированный тип TXMSPtr для хранения информации о выделенном блоке. При этом нулевое значение поля Allocated будет означать пустой и не инициализированный указатель на EMB, а единичное значение будет указывать на то, что указатель инициализирован для EMB с идентификатором в поле Handle и линейным адресом в поле LPtr, и что по завершении программы этот блок следует освободить. В данной структуре для поля Allocated выбран тип Word, хотя можно было применить тип Boolean. Это сделано для того, чтобы размер структуры TXMSPtr был чётным, да и к тому же кратен двум.
Затем следует объявление экспортируемых процедур и функций модуля.
{$G+}
unit XMS;
Interface
type
TLinePtr = record
case integer of
0: (AsPtr : pointer);
1: (AsInt : longint);
2: (LPart, HPart : word);
end;
TXMSPtr = record
Allocated, {0 - свободен (NIL),
1 - при завершении освободить }
Handle : word;
LPtr : TLinePtr;
end;
function TestX86 : byte;
function PMode : boolean;
function MemXMSAvail : word;
function MaxXMSAvail : word;
function InitXMS : boolean;
function GetXMS( var XPtr : TXMSPtr; Size : word ) : word;
procedure FreeXMS( var XPtr : TXMSPtr );
procedure WriteXMSWord( Dst : TXMSPtr; Offs : longint; W : word );
function ReadXMSWord( Src : TXMSPtr; Offs : longint ) : word;
procedure WriteXMSByte( Dst : TXMSPtr; Offs : longint; B : byte );
function ReadXMSByte( Src : TXMSPtr; Offs : longint ) : byte;
procedure WriteXMSLong( Dst : TXMSPtr; Offs, L : longint );
function ReadXMSLong( Src : TXMSPtr; Offs : longint ) : longint;
procedure MoveMemToXMS( Dst : TXMSPtr; var Buf; DstOffs, Count : longint );
procedure MoveXMSToMem( Src : TXMSPtr; var Buf; SrcOffs, Count : longint );
procedure MoveXMS( Src, Dst : TXMSPtr; SrcOffs, DstOffs, Count : longint );
В блоке реализации объявим структуру для внутреннего использования TEMBCopyRec, которую необходимо заполнять для копирования данных из одной памяти в другую. Например, для копирования данных из DOS-памяти в XMS-память, следует занести нуль в поле SrcHandle, а в поле SrcPtr занести указатель на буфер DOS-памяти, в поле DstHandle занести идентификатор EMB-блока XMS-памяти и в поле DstPtr указать смещение в байтах относительно начала EMB-блока. Для реверсной, то есть обратной пересылки данных из XMS-памяти в DOS-память, следует поместить в поле SrcHandle идентификатор EMB и в поле SrcPtr указать смещение в байтах относительно начала EMB, а в поле DstHandle поместить нуль, и в поле DstPtr поместить указатель на буфер DOS-памяти. Размер пересылки данных заносится в поле Counter, причём это значение должно быть чётно, иначе пересылка нечётного количества байт, например, 201 байта не состоится.
Implementation
type
{ структура для пересылки данных из нижней (DOS) памяти в верхнюю (XMS) и обратно }
TEMBCopyRec = record
Counter : longint; { чётное кол-во пересылаемых байтов, т.е. пересылка 201-го байта не состоится }
SrcHandle : word; { если 0 - копировать из обычной памяти, иначе из XMS-памяти }
SrcPtr : TLinePtr; { смещение источника в байтах при источнике XMS, иначе сегмент:смещение для источника DOS }
DstHandle : word; { если 0 - копировать в обычную память, иначе в XMS-память }
DstPtr : TLinePtr; { смещение получателя в байтах при получателе XMS, иначе сегмент:смещение для получателя DOS }
end;
Теперь объявим переменную XMM для хранения адреса драйвера, и переменную EMBCopy для осуществления всех операций по копированию данных из одной памяти в другую.
var
XMM : longint;
EMBCopy : TEMBCopyRec;
Теперь рассмотрим две функции, которые имеют лишь косвенное отношение к работе с XMS, но при этом будут полезны.
Код функции TestX86 реализует “официальный” метод фирмы INTEL по распознаванию типа процессора, и в качестве результата возвращает 0 если в машине установлен центральный процессор i8086, либо 1 если в компьютере установлен процессор i80286, либо 2 для i80386 соответственно. Не буду вдаваться в подробности данного метода, так как данные “шаманские разводы руками” в виде этого ассемблерного кода следует принять как должное.
{Return: 0 - 8086, 1 - 80286, 2 - 80386}
function TestX86 : byte; assembler;
asm
xor ax,ax;
push ax;
popf;
pushf;
pop ax;
and ax,0F000h
cmp ax,0F000h;
je @CPU_86;
mov ax,0F000h;
push ax
popf;
pushf;
pop ax;
and ax,0F000h;
jz @CPU_286
mov ax,2;
jmp @end
@CPU_286: mov ax,1;
jmp @end
@CPU_86: mov ax,0
@end:
end;
Вторая функция позволяет определить, в каком режиме находится центральный процессор. Результат False будет свидетельствовать о работе процессора в режиме реальных адресов (реальный режим), а True о том, что процессор находится в виртуальном режиме процессора 8086 (V86, то есть подвиде защищённого режима).
{Return: false - Real Mode, true - Protected Mode(V86)}
function PMode : boolean; assembler;
asm
smsw ax;
test al,1;
jnz @Pmode;
mov al,0;
jmp @end
@PMode: mov al,1
@end:
end;
Вот теперь мы подобрались к функциям, которые касаются непосредственно работы с XMS.
Процедура GetXMMAddr для внутреннего использования и позволяет получить адрес диспетчера функций драйвера XMS (HIMEM.SYS). Для этого в регистр AX заносится код функции $43 и код подфункции $10, и вызывается программное прерывание $2F, после чего в регистровой паре ES:BX (сегмент в ES, смещение в BX) будет получен адрес драйвера для дальнего вызова.
procedure GetXMMAddr( var APtr : longint ); assembler;
asm
mov ax,4310h;
int 2fh;
mov ax,es;
les di,APtr
mov es:[di],bx;
add di,2;
mov es:[di],ax
end;
Работу с драйвером следует начинать с вызова функции InitXMS, которая вовсе не инициализирует драйвер, а просто проверяет наличие драйвера XMS в памяти и пытается получить его адрес в переменную XMM. Если драйвер загружен, то функция возвращает True, иначе False. Для обнаружения драйвера в регистр AX заносится код функции $43 и код подфункции $00, и вызывается прерывание $2F. Если в регистре AL возвращено значение $80, то драйвер присутствует, иначе нет.
{Return: true – драйвер в памяти, false - XMS драйвер не найден}
function InitXMS : boolean;
var err : boolean;
begin
asm
mov ax,4300h;
int 2fh;
cmp al,80h;
je @yesXMS
xor ax,ax;
jmp @end
@yesXMS: mov ax,1
@end: mov err,al
end;
GetXMMaddr( XMM );
InitXMS := err;
end;
Вот теперь можно непосредственно заняться работой с XMS.
Для начала проведём ревизию свободной памяти. Для этого поместим номер функции 8 драйвера в AH, и вызовем диспетчер функций драйвера. В регистре AX будет возвращен размер максимального свободного EMB в килобайтах, а в регистре DX суммарный объём свободной XMS в килобайтах. Размер максимального свободного EMB можно получить функцией MaxXMSAvail, так как для удобства, в ней игнорируется значение регистра DX.
function MaxXMSAvail : word; assembler; {в Кбайтах}
asm
mov ah,8;
call [XMM]
end;
Для получения суммарного объёма свободной XMS при помощи функции MemXMSAvail вызываем ту же функцию драйвера, но в качестве результата возвращаем значение регистра DX.
function MemXMSAvail : word; assembler; {в Кбайтах}
asm
mov ah,8;
call [XMM];
mov ax,dx
end;
Следующие функции тоже для внутреннего использования. Первая из них GetEMB позволяет выделить EMB размером Size килобайт. Для этого в регистр DX заносим размер, в AH номер функции 9 драйвера. При возникновении ошибки в регистре AX будет возвращён нуль, а в регистре BL код ошибки. При успешном выделении EMB в регистре AX будет ненулевой значение, а в DX идентификатор выделенного EMB. В итоге, функция GetEMB при удачном выделении EMB возвращает нуль, иначе код ошибки, например, ошибка с кодом $0A0 означает, что не хватает XMS для выделения EMB затребованного размера.
{Return: 0 - Ok, $0A0 – не хватает XMS}
function GetEMB( Size : word; var Handle : word ) : word; assembler;
asm
mov dx,Size;
mov ah,9;
call [XMM];
cmp ax,0;
je @err
xor ax,ax;
jmp @end
@err: mov al,bl {error code to AL}
@end: les di,Handle;
mov es:[di],dx
end;
Процедура FreeEMB освобождает EMB, для чего в AH помещаем номер функции $0A, а в DX идентификатор освобождаемого EMB.
Возможная ошибка игнорируется.
procedure FreeEMB( Handle : word ); assembler;
asm
mov ah,0ah;
mov dx,Handle;
call [XMM]
end;
Ну и конечно, зачем нам нужен блок памяти, если в него нельзя пересылать и читать из него данные? Ответом на этот вопрос послужит функция CopyEMB. В регистре AH номер функции $0B, а регистровая пара DS:SI должна содержать указатель на структуру типа TEMBCopyRec. Поэтому для удобства я объявил переменную EMBCopy в сегменте данных, чтобы не пришлось изменять содержимое регистра DS в блоке реализации данной функции. Если в регистре AX возвращено нулевое значение, то произошла ошибка и её код находится в регистре BL. Иначе в AX ненулевое значение.
В итоге, функция CopyEMB при удачном копировании данных возвращает нуль, иначе код ошибки.
{Return : 0 - ok, <>0-error}
function CopyEMB( OffsEMBCopy : word ) : word; assembler;
asm
mov si,OffsEMBCopy;
mov ah,0bh;
call [XMM];
cmp ax,0
je @err;
xor ax,ax;
jmp @end
@err: mov al,bl {error code to AL}
@end:
end;
Иногда, возникает необходимость работать на прямую с линейным адресом EMB, например, в режиме реальных адресов. Для получения линейного 32-битного адреса выделенного EMB служит функция GetLinePointer. В AH номер $0C функции блокирования EMB, а в DX идентификатор. В регистровой паре DX:BX будет возвращён линейный адрес. Если в регистре AX возвращено нулевое значение, то произошла ошибка и её код находится в регистре BL. Иначе в AX ненулевое значение. При удаче функция GetLinePointer возвращает нуль, иначе код ошибки.
{Return: 0-ok,<>0-error}
function GetLinePointer( Handle : word; var LPtr : TLinePtr ) : word; assembler;
asm
mov dx,Handle;
mov ah,0ch;
call [XMM];
cmp ax,0
je @err;
xor ax,ax;
jmp @end
@err: mov al,bl {error code to AL}
@end: les di,LPtr;
mov es:[di],bx
add di,2;
mov es:[di],dx
end;
Ну и последняя внутренняя процедура по разблокированию EMB.
В регистр AH загружается номер функции $0D, а в DX идентификатор разблокируемого EMB.
procedure UnlockEMB( Handle : word ); assembler;
asm
mov ah,0dh;
mov dx,Handle;
call [XMM]
end;
Литература2. Д-р Джон М. Гудмэн. Управление памятью для всех – К.:Диалектика, 1996. – 520 с.
Продолжение следует…
© Владислав Демьянишин
Вы находитесь на официальном сайте Владислава Демьянишина - разработчика игры Dune IV (Dune 4). На нашем сайте Вы можете бесплатно скачать игры Dune IV (Dune 4), Battle City (Танчики с Dendy/Nintendo), читы к играм и многое другое. Также Вы можете скачать бесплатно программы и полезные утилиты. Все программы чистые, т.е. не содержат вирусов и иного вредоносного ПО.
Среди доступных программ есть мобильная читалка книг, менеджер переноса файлов с фото- и видеокамер на компьютер, текстовый редактор, WYSIWYG редактор, 3D аниматор, GIF аниматор, AVI аниматор, пакетный конвертор изображений, редактор электрических схем, программа для скриншотов, диспетчер тем рабочего стола и другие.
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ. Предлагаю также посетить Марья искусница - сайт о рукоделии (http://mariya-iskusnica.ru).
Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 26): 640К для Паскаля не предел
![]()
| ||||||||||||||||||||||||
|
||||||||||||||||||||||||
|