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
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
 

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

 

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

 

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

(продолжение)

 
Паскаль для новичковНу что, продолжим?
Функция GetXMS выполняет все необходимые действия по выделению EMB размером Size килобайт, инициализирует структуру указателя XPtr, помещая идентификатор выделенного EMB в поле Handle, в поле Allocated заносит единицу. Если процессор работает в реальном режиме, то функция пытается заполучить линейный адрес выделенного EMB, и если это удаётся, то заносит его 32-битное значение в поле LPtr. Если же процессор находится в режиме V86, то при вызове функции GetLinePointer может произойти ошибка выполнения задачи, так как режим работы V86 процессора свидетельствует о том, что загружен драйвер EMM386 либо программа работает в сеансе MS-DOS под Windows. Дело в том, что в этих двух случаях линейный адрес скорее всего не совпадёт с физическим адресом EMB, да и обратиться к нему по нулевому селектору в сегментном регистре и линейному адресу из программы в режиме V86 практически невозможно, так как произойдёт нарушение защиты.
 
Если функция возвращает нуль, то операция выделения EMB прошла успешно, иначе нет.
Помимо этого, данная функция выполняет копирование двух байт из памяти произвольного адреса, например, $0:$0 в область полученного EMB, чтобы восстановить в кэше дескрипторов предел в 4Гб для нулевого селектора. Это полезно сделать на тот случай, если программа выполняется в реальном режиме. Тогда при загрузке нулевого значения селектора в сегментный регистр DS или ES (кроме FS и GS) и 32-битного смещения, коим может быть линейный адрес EMB, в любом 32-битном регистре EAX, EBX, ECX, ESI, EDI, EBP можно обратиться непосредственно к области EMB для пересылки данных, минуя функцию CopyEMB драйвера, что даст четырёхкратное ускорение пересылки данных при условии, что пересылка будет выполняться двойными словами (по 4 байта).
 
{Size - в Кбайтах
Return: 0 – ok, <>0 - error}
function GetXMS( var XPtr : TXMSPtr; Size : word ) : word;
var P : pointer;
begin
GetXMS := $0ff;
XPtr.Allocated := 0;
if GetEMB( Size, XPtr.Handle )<>0 then exit;
P := Ptr( 0, 0 );
with EMBCopy do begin
         Counter := 2;
         SrcHandle := 0;
         SrcPtr.AsPtr := P;
         DstHandle := XPtr.Handle;
         DstPtr.AsInt := 0;
         end;
if CopyEMB( ofs( EMBCopy ) )<>0 then exit;
if not PMode then
 if GetLinePointer(XPtr.Handle,XPtr.LPtr)<>0 then;
XPtr.Allocated := 1;
GetXMS := 0;
end;
 
Процедура FreeXMS позволяет освободить EMB с идентификатором XPtr.Handle, который был ранее выделен функцией GetXMS. При этом выполняется разблокировка EMB, затем его освобождение и сброс в нуль поля Allocated структуры XPtr. При этом размер EMB указывать нет необходимости, так как драйвер самостоятельно ведёт учёт всех выделяемых EMB. Возможная ошибка игнорируется.
 
procedure FreeXMS( var XPtr : TXMSPtr );
begin
UnlockEMB( XPtr.Handle );
FreeEMB( XPtr.Handle );
XPtr.Allocated := 0;
end;
 
Теперь мы подобрались непосредственно к функциям пересылки данных.
Процедура MoveMemToXMS копирует чётное количество Count байт из нижней DOS-памяти Buf (источник) в EMB, представленный структурой Dst (получатель), по смещению DstOffs байт относительно начала EMB. Возможная ошибка игнорируется.
 
procedure MoveMemToXMS( Dst : TXMSPtr; var Buf; DstOffs, Count : longint );
begin
with EMBCopy do begin
         Counter := Count;
         SrcHandle := 0;
         SrcPtr.AsPtr := @Buf;
         DstHandle := Dst.Handle;
         DstPtr.AsInt := DstOffs;
         end;
if CopyEMB( ofs( EMBCopy ) )<>0 then;
end;
 
Процедура MoveXMSToMem копирует чётное количество Count байт из EMB, представленного структурой Src (источник), со смещением SrcOffs байт относительно начала EMB в нижнюю DOS-память Buf (получатель). Возможная ошибка игнорируется.
 
procedure MoveXMSToMem( Src : TXMSPtr; var Buf; SrcOffs, Count : longint );
begin
with EMBCopy do begin
         Counter := Count;
         SrcHandle := Src.Handle; 
         SrcPtr.AsInt := SrcOffs;
         DstHandle := 0;
         DstPtr.AsPtr := @Buf;
         end;
if CopyEMB( ofs( EMBCopy ) )<>0 then;
end;
 
Процедура MoveXMS копирует чётное количество Count байт из EMB, представленного структурой Src, со смещением SrcOffs байт в EMB, представленный структурой Dst со смещением DstOffs байт. Возможная ошибка игнорируется.
 
procedure MoveXMS( Src, Dst : TXMSPtr; SrcOffs, DstOffs, Count : longint );
begin
with EMBCopy do begin
         Counter := Count;
         SrcHandle := Src.Handle;
         SrcPtr.AsInt := SrcOffs;
         DstHandle := Dst.Handle;
         DstPtr.AsInt := DstOffs;
         end;
if CopyEMB( ofs( EMBCopy ) )<>0 then;
end;
 
Пришло время продемонстрировать на практике, как вся эта кухня работает ;o)
Для этого объявим XMS-указатели Ptr и Ptr2, для которых будет выделено по 1.5Мб на каждый (а кого нам стесняться? ;o) ).
После того, как нам удалось заполучить два EMB, выделяем память для динамической переменной Buf. В строку s1 заносим текст, который затем будем копировать куда не лень, а строку s2 делаем пустой.
 
После такой, казалось бы, громоздкой прелюдии, приступаем к перемещению данных. Сперва копируем образ строки s1 в Buf^, затем из Buf^ эти же данные копируем в EMB, представленный структурой Ptr. Потом следует наглядный пример, как можно копировать данные из одного EMB в другой EMB. После этого копируем данные из EMB, представленного структурой Ptr2, в строку s2 и отображаем её содержимое на экран. У меня всё сошлось, а у вас? ;o)
 
До этих пор было продемонстрировано копирование с нулевым смещением относительно начала EMB. Но бывает необходимость, когда данные следует копировать не с самого начала EMB, а с определённой позиции в нём. Тогда пригодится фрагмент примера, который копирует из EMB, представленного структурой Ptr, последние 8 символов в строку s3, после чего производится установка элемента строки s3[0], отвечающего за длину строки, и содержимое строки отображается на экране.
 
И в качестве последнего штриха освобождаем всю ранее полученную память. Вот собственно и код примера:
 
{$G+}
Uses XMS;
 
type TBuf = array [0..1000] of byte;
 
var Ptr, Ptr2 : TXMSPtr;
       s1, s2, s3 : string;
       SOffs : integer;
       Buf : ^TBuf;
 
begin
 
 
 
 
{ определяем тип центрального процессора }
 case TestX86 of
   0: s1 := 'CPU 8086';
   1: s1 := 'CPU 80286';
 else s1 := 'CPU 80386 or higher';
 end;
Writeln( s1 );
 
{ определяем текущий режим работы процессора }
if PMode then Writeln( 'CPU Mode: Protected (V86).' )
   else Writeln( 'CPU Mode: Real.' );
 
{ проверяем наличие драйвера в памяти }
if not InitXMS then begin
   Writeln( 'Error: XMS driver not found.' );
   halt;
   end;
 
{ проводим ревизию свободной XMS памяти }
Writeln( MemXMSAvail, ' kbytes' );
 
{выделяем 1500 Кб для нашего буфера в XMS-памяти}
if GetXMS( Ptr, 1500 )<>0 then begin
   Writeln( 'Error: Not enough memory.' );
   halt;
   end;
 
{ то же самое для Ptr2 }
if GetXMS( Ptr2, 1500 )<>0 then begin
   Writeln( 'Error: Not enough memory.' );
   halt;
   end;
 
{ динамический буфер в DOS-памяти }
GetMem( Buf, SizeOf( TBuf ) );
 
{ инициализируем строки перед пересылкой данных }
s1 := 'Hello friends!';
s2 := '';
Writeln( 's1= ', s1 );
 
{ копируем строку в Buf^ }
move( s1, Buf^, 16 );
 
{ так просто выглядит копирование образа строки из Buf^ в EMB }
MoveMemToXMS( Ptr, Buf^, 0, 16 );
 
{ так же просто копировать данные из одного EMB в другой }
MoveXMS( Ptr, Ptr2, 0, 0, 16 );
 
{ и не менее просто получить данные из EMB в строку }
MoveXMSToMem( Ptr2, s2, 0, 16 );
 
{ Ну что у нас получилось? Выглядит неплохо }
Writeln( 's2= ', s2 );
 
SOffs := 7;
{ копируем окончание строки из EMB с указателем Ptr, начиная со смещения SOffs, в строку s3 }
MoveXMSToMem( Ptr, s3[1], SOffs, 8 );
 
{ устанавливаем новую длину строки }
s3[0] := char( 8 );
 
{ Проверяем результат }
Writeln( 's3= ', s3 );
 
{ Освобождаем выделенные ранее EMB и проверяем освобождение ресурсов }
FreeMem( Buf, SizeOf( TBuf ) );
FreeXMS( Ptr );
FreeXMS( Ptr2 );
Writeln( MemXMSAvail, ' kbytes' );
end.
 
Теперь, я думаю, что на счёт длинных пересылок данных всё ясно. Правда, на веку программиста встречаются разные задачи, и может статься так, что в EMB будет целиком размещён, например, файл BMP (Windows BitMap) с его заголовком, и возникнет необходимость прочесть лишь отдельные поля заголовка, например, поля (по два байта) ширины и высоты графического образа. Ну не копировать же все 54 байта (ровно столько занимает заголовок BMP) по такому случаю, да и каждый раз готовить переменную-получатель такой длины не целесообразно. Гораздо проще читать по два байта прямо из EMB, правда если такие манипуляции будут выполняться в цикле многократно, то на максимальную производительность рассчитывать не стоит.
 
Для чтения слова из EMB со смещением Offs байт, составим функцию ReadXMSWord, возможную ошибку игнорируем.
 
function ReadXMSWord( Src : TXMSPtr; Offs : longint ) : word;
var W : word;
begin
with EMBCopy do begin
         Counter := 2;
         SrcHandle := Src.Handle; 
         SrcPtr.AsInt := offs;
         DstHandle := 0;
         DstPtr.AsPtr := @W;
         end;
if CopyEMB( ofs( EMBCopy ) )<>0 then;
ReadXMSWord := W;
end;
 
Для записи слова W в EMB послужит процедура WriteXMSWord.
 
procedure WriteXMSWord( Dst : TXMSPtr; Offs : longint; W : word );
begin
with EMBCopy do begin
         Counter := 2;
         SrcHandle := 0; 
         SrcPtr.AsPtr := @W;
         DstHandle := Dst.Handle;
         DstPtr.AsInt := offs;
         end;
if CopyEMB( ofs( EMBCopy ) )<>0 then;
end;
 
Основываясь на двух предыдущих подпрограммах составим ещё две подпрограммы для “ленивых”, то бишь на случай пересылки побайтно. Тогда функция ReadXMSByte читает байт со смещением Offs байт от начала EMB.
 
function ReadXMSByte( Src : TXMSPtr; Offs : longint ) : byte;
var w : word;
begin
w := ReadXMSWord( Src, Offs);
ReadXMSByte := byte( w );
end;
 
В свою очередь, процедура WriteXMSByte заносит байт B в EMB со смещением Offs байт.
 
procedure WriteXMSByte( Dst : TXMSPtr; Offs : longint; B : byte );
var w : word;
begin
w := ReadXMSWord( Dst, Offs );
w := (w and $0ff00) or B;
WriteXMSWord( Dst, Offs, w );
end;
 
Аналогичным образом, коим построены подпрограммы ReadXMSWord и WriteXMSWord, можно составить подпрограммы ReadXMSLong и WriteXMSLong для пересылки данных типа Longint, при этом счётчик пересылаемых байт должен быть равен четырём.
  
Чтобы понять, как эти функции работают, объявите глобальную переменную J : integer и добавьте в предыдущий пример перед кодом освобождения ресурсов следующий код:
 
for j := 1 to Length( s1 ) do
      Write( char( ReadXMSByte( Ptr, j ) ) );
 
  

А на последок я скажу…

  
Помимо способа пересылки данных при помощи функции CopyEMB есть ещё один способ копирования данных, через прямой доступ к памяти EMB. Его можно осуществить посредством загрузки нулевого селектора в любой из сегментных регистров DS или ES (кроме FS и GS, которые появились в i80386), и обращения к памяти по смещению в любом 32-битном регистре из EAX, EBX, ECX, ESI, EDI, EBP. Второй способ является наиболее производительным (в два и более раз), но имеет один существенный недостаток, а именно то, что такой способ будет работать только если процессор работает в реальном режиме, то есть когда линейный 32-битный адрес EMB соответствует его физическому адресу. Если же процессор работает в режиме V86 (при EMM386 или под Windows), то ни EMM386, ни Windows не захотят вернуть линейный адрес EMB. К тому же, попытка обратиться к памяти по смещению в 32-битном регистре превышающему 64К ($FFFF) повлечёт за собой прерывание, а точнее, исключительную ситуацию нарушения защиты, под номером $0D. Всё дело в том, что исключительная ситуация (сбой) обрабатывается процессором не так как обычное прерывание. То есть адрес возврата из обработчика сбоя (помещённый в стек) указывает на команду, сгенерировавшую данный сбой, а не на команду, следующую за ней. Сам же обработчик прерывания с номером $0D состоит из единственной машинной команды IRET. Таким образом, управление возвращается команде, вызвавшей сбой, и компьютер зацикливается (зависает).
 
Поэтому, несмотря на некоторую медлительность функции CopyEMB, всё же следует использовать её и подпрограммы, работающие через неё, которые были описаны выше. Это позволит создавать программы, стабильно и успешно работающие как в чистом MS-DOS (в реальном режиме процессора) и под драйвером EMM386 (режим V86 процессора), так и в сеансе MS-DOS под Windows.
 
А нам с Паскалем море по колено, да и горы по плечо! ;O)
 

Литература

1.     Диалоговая справочная система Norton Guide.
2.     Д-р Джон М. Гудмэн. Управление памятью для всех – К.:Диалектика, 1996. – 520 с.
 
Продолжение следует…
 
© Владислав Демьянишин
 
Вы находитесь на официальном сайте Владислава Демьянишина - разработчика игры Dune IV (Dune 4). На нашем сайте Вы можете бесплатно скачать игры Dune IV (Dune 4), Battle City (Танчики с Dendy/Nintendo), читы к играм и многое другое. Также Вы можете скачать бесплатно программы и полезные утилиты. Все программы чистые, т.е. не содержат вирусов и иного вредоносного ПО. Предлагаю также посетить Марья искусница - сайт о рукоделии (http://mariya-iskusnica.ru).
 
 
 

Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 28): 640К для Паскаля не предел 2 (продолжение)
 
 
 
12
 
ВКонтакте
Facebook
 
 
 
На главную страницу На предыдущую страницу На начало страницы
 
 
Украинский портАл Украина онлайн Рейтинг@Mail.ru Рейтинг Сайтов YandeG Rambler's Top100