Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Система";
Текущий архив: 2002.10.21;
Скачать: [xml.tar.bz2];

Вниз

Работа с большой областью оперативки из Ассемблера   Найти похожие ветки 

 
Yury Yakhno   (2002-08-07 23:46) [0]

Здравствуйте, уважаемые мастера.
Знает ли кто-нибудь решение этой проблемы:
Есть неразрывная область оперативной памяти размером до нескольких десятков мегабайт. На неё указывает Pointer. В ассемблерном коде нужно получить каждый байт по очереди. Проблема в том, что если написать:

var
M:Pointer;
N:Cardinal;
.
.
.
GetMem(M, N);
asm
.
.
.
mov ESI,M
.
.
.
inc ESI
mov AL,[ESI]
.
.
.
end;


то это работает на маленьких областях оперативной памяти, но если области большие, то надо ещё переходить через границу сегмента данных.
Кто-нибудь сталкивался с этой проблемой? Не подскажет ли кто-нибудь, например, как узнать какой сегментный регистр используется для моих данных (ds, es, gs, fs), или какая модель адресации используется?


 
Anatoly Podgoretsky   (2002-08-07 23:49) [1]

Какая версия Дельфи?


 
Слесарь Матерящийся   (2002-08-08 00:25) [2]

Модель flat. [Местами stdcall. Ой. Шютка :)]
32 бита. плоский мир. про сегментные регистры просто необходимо забыть.
и что означает:
"работает на маленьких областях оперативной памяти, но если области большие, то надо ещё переходить через границу сегмента данных."
-- совершенно непонятно. Поясните, pls.


 
Юрий Зотов   (2002-08-08 08:56) [3]

Не только сегментые регистры - даже ассемблер не нужен. Простое приведение Pointer к DWORD решает проблему адресной арифметики.


 
Yury Yakhno   (2002-08-08 13:53) [4]

> Anatoly Podgoretsky © (07.08.02 23:49)
> Какая версия Дельфи?

Delphi 6

> Слесарь Матерящийся (08.08.02 00:25)
> Модель flat. [Местами stdcall. Ой. Шютка :)]
> 32 бита. плоский мир. про сегментные регистры просто необходимо забыть.
> и что означает:
> "работает на маленьких областях оперативной памяти, но если области
> большие, то надо ещё переходить через границу сегмента данных."
> -- совершенно непонятно. Поясните, pls.

Насколько я знаю, Pointer - 32 битное смещение относительно сегмента данных программы. Когда я заношу указатель в ESI, он уже имеет какое-то значение. Так вот если выделить себе мегов сто и зациклить ESI на inc, не достигнет ли он своего максимального значения, а потом обнулится?
Скорее всего, я просто чего-то не догоняю, но регулярно получаю Access Violtation при обращении к [ESI] через некоторое время после начала работы.

> Юрий Зотов © (08.08.02 08:56)
> Не только сегментые регистры - даже ассемблер не нужен.
> Простое приведение Pointer к DWORD решает проблему
> адресной арифметики.

Разумеется, но я с каждым байтом ещё определённые действия произвожу. Ассемблер мне для этого нужен. А про DWORD я выше писал.


 
Digitman   (2002-08-08 14:04) [5]

DS = ES = CS = SS

размер "сегмента", о коем ты так беспокоишься, формально может достигать 4GB - это предостаточно для того , чтобы надолго забыть обо всем касаемом "сегментов"


 
Слесарь Матерящийся   (2002-08-08 14:46) [6]

>>>Насколько я знаю, Pointer - 32 битное смещение относительно сегмента данных программы.

В принципе так. Но видимо, вы путаете две модели памяти -- "традиционную" сегментированную для DOS и flat (protected mode).

В режиме адресации flat вы (в принципе) можете обращаться к любому адресу в рамках 0..4 Гб. Для этого вы можете использовать индексные регистры или ebx.

Модель flat подразумевает перекрытие сегментов адреса и данных в одном адресном пространстве.

Так, например (например!), если по адресу 0x04567870 в сегменте кода записан байт 0x45, то он будет виден в сегменте данных по тому же (0x04567870) адресу.
Разумеется, сейчас мы не учитывали принципы работы виртуальной памяти и т.п. Речь шла только об адресации.

--------

>>>Так вот если выделить себе мегов сто и зациклить ESI на inc, не достигнет ли он своего максимального значения, а потом обнулится

В реальном режиме DOS-программ такое могло быть. Но в protected mode работает механизм защиты памяти.
Вы можете обращаться т_о_л_ь_к_о к заранее выделенной области памяти.
Если вы обратились "мимо", сработает механизм защиты и вы получите msg "Access violation".
Просто контролируйте, чтобы указатель не переходил границы выделенной области.
Так, к примеру, если выделенный фрагмент расположен по адресу
0x00770000 и имеет длину 0x00004000 байт, то диапазон памяти, к которому можно обращаться -- 0x00770000..0x00773FFF

//


 
Digitman   (2002-08-08 15:11) [7]

Добавлю только к выступлению уважаемого Слесаря, что обращение по смещению в любом текущем сегменте за пределы 64к совершенно необязательно требует работу в защищенном режиме. На современных машинах это вполне успешно реализуемо и в реальном режиме DOS, выполняемой CPU не ниже i80386. При этом, установив единожды нужные сегментные регистры и имея достаточно памяти (при окрытой 20-й адресной линии) можно запросто обращаться к любому участку физ.памяти, не заботясь более о содержимом сегментных регистров


 
Сергей Медяник   (2002-08-09 23:13) [8]

Я тут сдул пыль со справочника по ASM. Небольшое дополнение и корректива словам Слесаря (Digitman © - ты заблуждаешься). В ProtectedMode сегментные регистры загружаются не базовыми адресами, а селекторами дискрипторов. Дискрипторы - структурки в памяти, которые и описывают назначение, базовый адрес, размер страницы памяти и количество памяти в этих страницах, права доступ для блока памяти (код, стек, данные) => этим и реализуется ЗАЩИТА - процессор сам не залезет указателем данных в область кода или стека, например. Дискрипторы есть глобальные, используются ядром операционки, и локальные - прикладной программой. А Pointer, собственно указывает смещение.
Так что в принципе, обращение может быть действительно к любому байту в выделенной области, заковырька может быть в виртуальной памяти. Этот механизм реализуется программно ядром ОС. Win управляет страничным обменом сама => может быть разница между физической организацией 100 блоков по 1Mb и 1 блоком 100Mb.
TO Yury Yakhno: Попробуй выделять себе память не более доступной физической или, лучше небольшими (относительно) блоками. Определение объема памяти я подсмотрел на этом форуме, цитирую:

Function GetRAMSize:integer;
var MS : TMemoryStatus;
Begin
GlobalMemoryStatus(MS);
Result := MS.dwTotalPhys;
end;


dwTotalPhys - Полный объем ОЗУ (т.е. физической памяти)
dwAvailPhys - Свободный объем ОЗУ (как правило небольшая величина)
dwTotalVirtual - Полный объем виртуальной памяти
dwAvailVirtual - Свободный объем виртуальной памяти
dwMemoryLoad - Процент использования памяти (0-не используется, 100-используется вся)
dwTotalPageFile - Общий размер данных (в байтах), которые могут быть сохранены в файле подкачки (но это не является его размером на диске !!)
dwAvailPageFile - Доступный объем в файле подкачки

Решение не фонтан, конечно, прийдется управлять самому переходами между блоками, но я сейчас сам ломаю голову над подобной проблемой (нужно расположить в памяти огромное количество данных и иметь возможность их быстро перегруппировывать, но управлять блоками хочется самостоятельно, без New, GetMem и прочего стандартного - оно тормозное для моей задачи).
Попутно вопрос, а сколько всего физической памяти у тебя и какая OS? (W2k и W98 могут по разному реагировать на такое насилие :)


 
Yury Yakhno   (2002-08-10 14:23) [9]

Спасибо ВСЕМ за объяснения. Дело в том, что мой справочник по ассемблеру несколько оторван от системы. В нём описаны механизмы работы сегментных регистров, но нет ни слова о том, что с ними делает Windows. Просмотрев свой ассемблерный код и не найдя там ничего подозрительного, я подумал на сегменты. Что ж, Вы меня почти убедили, что я ошибался. Осталось выяснить причину "Access Violtation". Если я не смогу сделать это сам, я выложу здесь проблемный кусок программы.

2 Сергей Медяник

> Этот механизм реализуется программно ядром ОС. Win управляет страничным обменом
> сама => может быть разница между физической организацией 100 блоков по 1Mb и 1
> блоком 100Mb.

Я эту разницу вижу только в строгой неразрывности 1 блока по 100 мб, о чём я говорил в начале.

> Попробуй выделять себе память не более доступной физической или, лучше небольшими
> (относительно) блоками. Определение объема памяти я подсмотрел на этом форуме, цитирую:

Собственно, я так и поступаю. Для определения свободной памяти я использую ту же функцию, о которой говоришь ты.
Кстати, там перед строкой GlobalMemoryStatus(MS) нужно ещё написать MS.dwLength:=SizeOf(MS).

> Решение не фонтан, конечно, прийдется управлять самому переходами между блоками, но
> я сейчас сам ломаю голову над подобной проблемой (нужно расположить в памяти огромное
> количество данных и иметь возможность их быстро перегруппировывать, но управлять
> блоками хочется самостоятельно, без New, GetMem и прочего стандартного - оно тормозное
> для моей задачи).

Мне кажется, что для моей задачи подойдёт один большой кусок оперативки. В том, что с ним можно работать без глюков, я уверен. Интересно узнать, как :-)

> Попутно вопрос, а сколько всего физической памяти у тебя и какая OS? (W2k и W98
> могут по разному реагировать на такое насилие :)

Физической памяти у меня 256 MB, так что а могу себе позволить подобные массивы :-)
Операционка - Win2k, но я запускал exeшник и под Win95 - та же ошибка.


 
Anatoly Podgoretsky   (2002-08-10 14:43) [10]

Для тебя эта память будет неразнывная, зотя на самой деле не обязательно так, да и в разные моментц времени она может оказываться в разных местах оперативной памяти


 
Сергей Медяник   (2002-08-12 03:07) [11]

ЕСТЬ РЕШЕНИЕ!!!
5 часов копания в Helpe и десятки эксперементов привели к следующему:
Чтобы обратиться к любому байту из выделенных GetMem нужно писать:

GetMem(PointerFromGetMem)
asm
//ОБЯЗЯТЕЛЬНО! Именно отсюда Access Violation...
push eax
push ebx

//Загружаю в EBX адрес, содержащийся в переменной.
mov ebx, DWORD PTR PointerFromGetMem
///// ВМЕСТО ebx, PointerFromGetMem !!!!!!!!!!
//EBX содержит нужный указатель, индексируй как хошь!!!
mov al, b
mov [ebx], al

//ОБЯЗЯТЕЛЬНО! Именно отсюда Access Violation...
pop ebx
pop eax
end;


Так же разобрал ЕЩЕ 3 способа выделения памяти через WinAPI. У каждого свои особенности (Через кучу, виртуальная, разделяемая процессами).
Есть программка для демонстрации. Кому надо, пишите на мэйл - скину (200kb ZIP).
Рекомендация по работе с большими блоками вообще: Не обрабатывайте сразу много в одной процедуре. Если выделили больше доступной физической - свапит страшно, аж WinAmp гавкает!

Короче, я родственник этого ассемблера, этого виндовз, этой виртуальной машины, ее создателей и БИЛА ГЕЙТСА в частности!!!
P.S. Сделано на: Duron 600, 196Mb, Win2k, Delphi6, проверял на блоках по 200 метров (больше просто тормознее, принципиально ничего не меняется).


 
mumu   (2002-08-12 05:22) [12]

Странно. Такой простой вопрос и столько словопрений.
Кидаем на форму кнопку и вставляем обработчик:

procedure TForm1.Button1Click(Sender: TObject);
var
Size: Integer;
Buffer: PChar;
t1,t2,t: LongInt;
begin
// выделяем 500 000 000 байт
Size := 500000000;
GetMem(Buffer, Size);
// для приличия можно перехватывать EOutOfMemory
t1:=GetTickCount;
try
asm
mov ecx,size
mov esi,buffer
@l1:
mov al,[esi]
// делаем, что хотим с al
//-------------------------
inc al
// ------------------------
mov [esi],al
inc esi
loop @l1
end;
t2:=GetTickCount;
Showmessage("Затрачено времени на обработку"+#13+
IntToStr(size)+" байт"+#13+
IntToStr(t2-t1)+" миллисекунд.");
finally
FreeMem(Buffer);
end;
end;

Если файл подкачки определяется системой, то она автоматически увеличит его до нужных размеров (верно для w2k и w9x).
Если в объем виртуальной памяти фиксирован и меньше выделяемой памяти, то для:
w2k получим сообщение об увеличении файла подкачки
w9x сообщение об ошибке выделении памяти.
На обработку блока памяти ушло примерно 20-22 сек.
Сделано на: PIII 866, 256Mb, Win2k Pro+SP2, Delphi6.

Не вижу кода, но рискну предположить, что у тебя ошибка в ассемблерной вставке.
---------------
С уважением, mumu


 
Digitman   (2002-08-12 09:25) [13]

>Сергей Медяник

В чем же я заблуждаюсь, позволь полюбопытствовать ?

p.s.

Про селекторы - заметь - я ни слова ни сказал, благо прекрасно понимаю разницу между сегментным адресом и селектором сегмента в сегментном регистре.


 
Сергей Медяник   (2002-08-12 10:26) [14]

>Digitman ©
Деталь, насчет сегментных регистров. Туда пишустся СЕЛЕКТОРЫ дискрипторов (их может быть несколько тысяч в системе и меняются они системой с бешенной скоростью).

>mumu
Попробуй свой код в действии. (см. Код автора топика и мою коррективу).


 
mumu   (2002-08-12 10:38) [15]

>Сергей Медяник
Именно с работающего проекта и снято.
А как же иначе я стал бы советовать???
---------------
С уважением, mumu


 
Digitman   (2002-08-12 11:18) [16]

>Сергей Медяник

Вот спасибо тебе за "новость" ! Как раз вот этого я и не знал)

Может и не стоило бы дополнять, но я вообще-то о другом сказал, ты вникни в мое дополнение.
А сказал я , что для доступа к любому участку физ.памяти вовсе необязательна работа в PM i80x86 - сие возможно и в RM, когда сег.регистры интерпретируются CPU обычным для MSDOS и ее приложений способом : как 16-разрядный адрес параграфа, а не как селектор сегмента.


 
mumu   (2002-08-12 11:38) [17]

2Digitman ©

Но ведь в реальном режиме DOS (REAL-ADDRESS MODE)
используется 16-битный IP. Как же можно вызезти
за пределы 64 Кб?


 
Shaman_Naydak   (2002-08-12 11:54) [18]

> mumu и Сергей Медяник
Выдержка из помощи
An asm statement must preserve the EDI, ESI, ESP, EBP, and EBX registers, but can freely modify the EAX, ECX, and EDX registers.
А EAX тебе пришлось скорее всего сохранять тоже из-за оптимизатора..
а с mumu как раз наборот.. повезло, что регистры не были нужны, потому и сбоев не было



 
Digitman   (2002-08-12 12:03) [19]

А я и не говорил конкретно про соответствующие возможности для расширенной адресации процессором инструкций исполняемого кода.

Зато ничто не мешает, скажем, успешному выполнению такого вот блока :

mov es, ...
mov ds, cs
mov esi, offset src_offset
mov edi, offset dst_offset
mov ecx, dwords_count
cld
rep movsd

при этом в физическое АП осн.памяти (предел "досягаемости" cs:ip при адресации инструкций в RM/VM ) в один прием (без переключения сегментных адресов) можно скопировать сколь угодно
большой блок данных из АП расширенной памяти (и далее передать управление в нужную точку, если того требует использование данной возможности при организации, скажем, нестандартного overlay-механизма)


 
mumu   (2002-08-12 12:06) [20]

2Shaman_Naydak ©:
Именно так. Спасибо за подсказку. Регистры в стек и прения закрыты.
(Вот она -- сила документации :)))
---------------
С уважением, mumu


 
Yury Yakhno   (2002-08-12 13:32) [21]

Спасибо всем, принимавшим участие в решение проблемы. Я отловил все глюки, и теперь всё работает.
Сделаем выводы:
- сегменты памяти не при чём
- с файлом подкачки разбирается Windows
- регистры надо спасать перед вставкой (в начале разработки можно писать pushad и popad, чтобы не мучаться)


 
Сергей Медяник   (2002-08-12 16:35) [22]

>Digitman © и остальным
Я признаю, что мог погорячиться с навязыванием своего представления о дискрипторах и прочем. Хотя я просто пересказал описания в книге, замечу, что там был ЧИСТЫЙ Asm, то есть в листингах описывались все механизмы управления памятью (в Win и Delphi за нас это делает ядро) => я наплел лишнего. Тем более, что экспериментально выяснил (Protected Mode): память действительно линейная для процессора (включая файл подкачки), более того начинается с 0, то есть необходимо заботиться не о DS, ES и т.п., а о верном базовом адресе при отсчете типа [EBX]. При этом Win сам распределяет блоки памяти (назначая им правила доступа), постранично свапит и следит за фрагментацией. Каждому блоку памяти, откушенному процессом соответствует Handle, по которому Winda и ориентируется в его расположении, размере, правах. Кстати, в WinAPI я вообше не обнаружил дискрипторов, только Handlы.
> mumu

Если работает, спорить не буду. Дело в том, что эти "DWORD PTR", которыми мой код отличен от Yury Yakhno играет ключевую роль, я так понял, для нетипизированных указателей
Mov ebx, VarOfPointer
записывает в ebx не значение указателя из VarOfPointer, а адрес переменной VarOfPointer в сегменте данных программы => попытка обратиться к 100Mb-му блоку там где всего 4 байта ПРОСТО ОБЯЗАНА ВЫКИНУТЬ ОШИБКУ.

> Shaman_Naydak ©
EAX сохранил с перепугу и недосыпу :-))) - это действительно необязательно.

>Digitman ©
В Real Mode дополнительные мегабайты использовались через маленькое окошко памяти, банки переключались через какой-то порт. Удобный способ давали дрова типа HIMEM.SYS - просто вызываешь сервис через SOFT-прерывания.
Попутно вопрос: ты уверен, что RealMode позволяет использовать 32-битные регистры. Я не ехидничаю, просто интересуюсь - не уверен, что это так.

>mumu
О простоте вопроса. Тут недавно обсуждали как от String отрезать последние 3 символа, так что здесь решается глобальная проблема! :-)))))


 
limon   (2002-08-12 18:48) [23]

> Сергей Медяник (12.08.02 16:35)
и не только RM, но даже 16-разрядный сегмент кода имеет право на использование 32-х р регистров, т.к. все зависит от типа кодового сегмента.
для USE16 при использовании EAX... добавляется префикс команды (если не ошибаюсь 0х66), для USE32 - соответственно - наоборот.



 
Yury Yakhno   (2002-08-12 21:00) [24]

> Сергей Медяник
> Если работает, спорить не буду. Дело в том, что эти "DWORD PTR",
> которыми мой код отличен от Yury Yakhno играет ключевую роль, я так
> понял, для нетипизированных указателей
> Mov ebx, VarOfPointer
> записывает в ebx не значение указателя из VarOfPointer, а адрес переменной
> VarOfPointer в сегменте данных программы => попытка обратиться к 100Mb-му
> блоку там где всего 4 байта ПРОСТО ОБЯЗАНА ВЫКИНУТЬ ОШИБКУ.

Нет, записывается именно значение VarOfPointer. Какой это указатель всё равно. Это легко можно проверить при пошаговой трансляции.

2 All

Могу сказать, что из всех Ваших примеров у меня была самая тупая ошибка - неправильно работал внутренний счётчик, поэтому прога пролетала весь буфер и лезла дальше. При этом я не забывал сохранять все регистры в стеке. Теперь всё работает, как я уже писал, так что есть работающая версия.
Всё равно, на мой взгляд, здесь многие узнали что-то новое о работе с ассемблером в Delphi :-)


 
Сергей Медяник   (2002-08-13 09:14) [25]

>Yury Yakhno

> Нет, записывается именно значение VarOfPointer. Какой это
> указатель всё равно. Это легко можно проверить при пошаговой
> трансляции

Поверю на слово, но у меня рабочий код от нерабочего отличался только этим.

И с моей стороны всем спасибо!


 
Shaman_Naydak   (2002-08-13 13:23) [26]

> ALL
Кстати, всем желающим знать кухню работы различных режимов работы процессора и адресаций советую прочесть книжку
Процессоры Pentium 4, Athlon и Duron.

Не помню чье издательство. Правда, книжка не для новичков ;)


 
Dmk   (2002-08-14 02:48) [27]

Так, на всякий случай. Пример отображения 32 битного изображения зеркалом.

//...

{



Страницы: 1 вся ветка

Форум: "Система";
Текущий архив: 2002.10.21;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.008 c
1-68693
Shadow
2002-10-09 14:26
2002.10.21
Замена кода нажатой клавишы.


1-68765
KVBr
2002-10-11 15:56
2002.10.21
Загрузка приложения


14-68976
DenKop
2002-09-28 15:19
2002.10.21
Java


14-68921
Dimich1978
2002-10-01 12:00
2002.10.21
Все на помощь...по мере возможности


3-68644
Roma111
2002-09-30 13:56
2002.10.21
Проверка имени и пароля





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский