http://sulfurzona.com/
News
Service
Magazine
Software (Battle City Game, Wallpaper manager, Superpad, VG-NOW, Puzzle Game, Netler Internet Browser, ..)
Dune Game (Dune III, Dune IV, Cheats, Forum, ..)
Games free
Turbo Pascal (Assembler, Docs, Sources, Debbugers, ..)
Books (Docs for developers)
Forum
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
 

Паскаль для новичков (часть 26)

 

Спрашивали? Отвечаю…

 

640К для Паскаля не предел

 
Паскаль для новичков
В предыдущих статьях я упоминал о том, что Паскаль-программе может быть доступна вся свободная оперативная память, доступная операционной системе. Как известно, Turbo Pascal предназначен для проектирования программ, работающих под MS-DOS, которой может быть доступно не более 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;
 
 

Литература

1. Диалоговая справочная система Norton Guide.
2. Д-р Джон М. Гудмэн. Управление памятью для всех – К.:Диалектика, 1996. – 520 с.
 
Продолжение следует…
 
© Владислав Демьянишин
 
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на 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К для Паскаля не предел
 
 
 
43
 
ВКонтакте
Facebook
 
 
 
На главную страницу На предыдущую страницу На начало страницы
 
 
Украинский портАл Украина онлайн Рейтинг@Mail.ru Рейтинг Сайтов YandeG Rambler's Top100