Глава 11. Работа с памятью.

Память человека есть лист бе­лой бумаги. Иногда напишешь хорошо, а иногда дурно.

Козьма Прутков.

Программа (программный код, данные и стек) после загрузки в память занимает лишьчасть (иногданезначительную) еесвободного пространства. Естественно, что в нашей власти использовать доступную память по своему усмотрению. О том, как сде­лать это корректно, будет рассказано в данной главе. Сразу оговорюсь, что речь идет о так называемой обычной памяти (английский термин conventional), мы назвали ее ба­зовой (см. главу 2). О расширенной (extended), дополнительной (expanded) и других видахпамяти в операционной системе MS DOS речь пойдет вдругихглавах (см. главу 22, а также главы 5 и 20).

I.

Основу для манипуляции памятью составляют три функции 21 Н-ro прерывания: 48Н, 49Н, 4АН. В главе 1 мы говорили о блоковой структуре памяти, с которой рабо­тает БО&Данные функциикакраз и манипулируютуказанными блоками памяти. Ниже дается подробное описание данных функций. Конкретный пример использо­вания функций 48Н и 49Н Вы сможете найти в главе 12 (см. также конец главы 10). Блоками памяти можно манипулировать, и минуя указанные функции (вручную). Это достаточно сложная процедура, в главе} 2 Вы сможете познакомиться с приме­рами такой манипуляции.

Итаквотэти функции:

48Н-вьгделяетблокпамяти. Размер запрашиваемой памятидолжен быть в ВХ Если блок такого размера существует, то в АХ возвращается сегментный адрес выделенного блока. ЕслифлагСустановлен,товАХпомещаетсякод ошибки, авВХ—максимально доступный объемпамяти. Размеры блоков памяти измеряются в параграфах.

49Н - освобождает блок памяти, который становится доступным для других про­цессов. В ES должен находиться сегментный адрес освобождаемого блока. Если флаг С установлен, то в АХ содержится код ошибки.

4АН - сжимает или расширяет выделенный блок. В ES—сегментный адрес блока, в ВХ—желаемый размер. Если флаг С установлен, то в АХ—код ошибки, авВХ— доступный наибольший блок (при попытке расширения).

Когдазаггускаетсяпрограммадоейавтоматическираспределяетсявесьдоступный объемпамяти. ОднакоеслиВыпожелаетезапуститькакую-тодругуюпрограммуспо-мощью стандартных ООЗ'овскихпроцедур, Вам придется освободить часть памяти.

П.

Оставим    в стороне запуск программы и поговорим подробнее     как можно

использовать свободную память. Первый вопрос, который возникает в этой связи, - кадобраться до свободной памяти, находящейся в старших адресах? Ответна этот вопрос состоит из двух частей:

1. Нужно знать параграф, откуда эта память начинается.

2. Для того чтобы знать параграф, необходимо, чтобы среди сегментов програм­мы был один пустой, начинающийся с буквы, которая идет в алфавите после всех первых букв других сегментов. Например:

ZSEG SEGMENT ZSEG ENDS

Если такой сегмент есть, то необходимо транслировать программу с ключом /а (или использовать директиву В этом случае все сегменты будут выстрое-

ны по алфавиту, и пустой сегмент окажется последним. По умолчанию MASM рас­полагает сегменты в том порядке, как они идут в тексте программы, поэтому можн о поместить ZSEG в конце программы и транслировать ее без всяких опций. Выпол­нив две команды: MOV AX,ZSEG и MOVES.AX, мы получим в ES как раз параграф, откуда начинается свободная память. Теперь ее можно свободно использовать. По­мните только, что память ограничена, и если Вы намерены написать хорошую про­грамму, которая бы контролировала свои возможности и резервы, то лучше пойти другим путем. Он состоит в следующем. Нужно урезать размер отведенной области до размеров самой программы. После этого Вы можете запрашивать необходимую память с помощью 48Н-Й функции. В случае, если затребованная память слишком велика, то функция возвратит в ВХ свободное количество памяти в параграфах. Та­ким образом, Вы всегда сможете контролировать ситуацию. Рассмотрим, как проис­ходит урезание памяти.

; при запуске программы ES и DS   указывают на начало PSP ; будем предполагать,  что значение ES   не менялось27

MOV   АХ,ZSEG

MOV BX,ES

XCHG AX,BX

SUB BX,AX

MOV   AH,4AH

INT 21H

He забывайте, что транслировать программу надо с ключом /а. Сегмент может располагаться как в начале, так и в конце программы. Проблема может возникнуть в случае компоновки нескольких модулей, но разговор об этом мы отложим до гла­вы 13.

До сих пор мы говорили об ЕХЕ-программах. Рассмотрим, как обстоят дела в слу­чае СОМ-программ. Вся СОМ-программа помещается в один сегмент. При запуске программы все четыре сегментных регистра будут указывать на начало этого сетаен­

" Функция DOS 62H возвращает В ВХ сегментный адрес PSP выполняющейся программы.

 На конец сегмента будет указывать регистр стека - SP, стек будет расти в сторону программы. Все адресное пространство за текущим сегментом будет в Вашем распо­ряжении. В главе 4 (см. Рис. 4.6) приведен пример использования этого пространства. Можноиспользоватьпространствоинепосредственнозапрограммой.Дляэтогодос-таточно в конце программы поставить метку и обращаться к адресам относительно этой метки. Только надо иметь в виду, что при этом Вы попадаете непосредственно в область стека со всеми вытекающими отсюда последствиями. Впрочем, стек можно расположить и в другом месте.

III.

Перейдем к рассмотрению вопроса запуска одной программы из другой. Данная проблема решается с использованием функции DOS 4BH. Эта функция позволяет не только запускать ЕХЕ или СОМ-программы, но и загружать оверлей. Что касается оверлеев, то здесь можно довольно легко обойтись без указанной функции, что мы сейчас и сделаем.

Рассмотрим сначала программу на Рис. 11.1, которая и являет собой простейший пример оверлейного модуля. По сутидела, это обычная СОМ-программастой лишь разницей,чтоздесьстоитОКО0,анеО1Ш 1(ЮН. Ноэтодолжнобытьпонятно,ведьу оверлея нет PSP. После работы редактора связей LINK. ЕХЕ необходимо преобразо­вать ЕХЕ-модуль К СОМ-формату (не обязательно с расширением СОМ). Второй мо­мент, на который я хотел бы обратить Ваше внимание, это работа с сегаентными реги­страми. После передачи управления оверлею на его сегмент будет указывать CS. Ос­тальные сегменты будут иметь значения такие, какие имели в основной программе. В нашем примере мы загружаем оверлей в сегмент данных, на который указывает DS (хотя могли бы загрузить и в адресное пространство за программой). Воспользовав­шись этим, мы не переопределяем DS. Правильнее было бы поместить в DS значение . из CS, а в конце, естественно, восстановить E>S.

CODE SEGMENT

ASSUME CS:CODE

ORG О

BEGIN:

LEA DX,CS:TEXT MOV AH, 9

INT 21H

RETF

TEXT DB  'Привет',13,10,'$'

CODE ENDS END

Рис. ll.l. Оверлей к программе на Рис. ll.2.

Рассмотрим теперь Рис. 11.2. Обращаю Ваше внимание на то, что оверлей мы считываем как обычный файл. Как можно заметить, для создания оверлейной струк-туры, вообще говоря, не обязательно пользоваться специальными средствами (фун­кция 4ВН). Используя такой метод, можно создать простейший менеджер управле­ния оверлеями28.

DSEG SEGMENT

DB 18 DUP(O) /буфер для оверлея

PATH DB 'PR1.COM',0 /имя оверлея

DSEG ENDS

SSEG SEGMENT STACK

DB 30 DUP(?) SSEG ENDS CODE SEGMENT

ASSUME CS:CODE,  DS: DSEG, SS;SSEG

BEGIN:

MOV AX,DSEG MOV DS,AX MOV AH, 3DH MOVAL, 0 LEA  DX,PATH

INT 21H /открываем оверлей для чтения

JC EXIT

MOV BX,AX

XOR DX,DX

MOV AH, 3FH

MOV CX, 18

INT21H /читаем оверлей в буфер

MOV АН, ЗЕН INT 21H

call dword ptr   CS:[OVERL]     /осуществляем запуск /процедуры в оверлее EXIT:    /ВЫХОД из программы M0V АН,4СН

INT 21H .

28 Менеджеруправленияоверлеямивсовременныхязыкахвысокогоуровня-довольнослож­ная процедура. В ее задачу входит обслуживание кольцевого оверлейного буфера. Проблема заключается том, что при вызове процедуры из оверлея менеджер должен определить, не находится ли данный оверлей уже в памяти - если находится, то с диска читать не надо. Если в памяти его нет, то следует прочесть его в буфер. Но тут возникает вторая проблема: в буфере может не быть места. Следовательно, какой-то другой оверлей должен быть вытолк­нут из очереди. Часто таким оказывается первый из очереди. Однако современные оверлей­ные менеджеры «умудряются» еще следить за частотой вызова того или иного оверлея, и вытолкнутым оказывается редко вызываемый оверлей. Добавьте сюда еще постоянную на­стройку адресов, по которым вызываются оверлейные процедуры. Как видим, все не так просто.

; полный адрес/  іде будет располагаться оверлей OVERL Ш о, ,.г. .. ; смещение <.\\А.

DW SEG PATH ; сегмент

CODE ENDS

END BEGIN

Рис. 11.2. Пример запуска оверлея (см. Рис. 11.1) без использования функции 4ВН.

TV.

Рассмотрим работу функции 4ВН. силу важности и сложности данной функции

здесь дается ее полное описание.

АН 4ВН

DS:DX адрес строки с именем программы, строка может содержать полный путь к каталогу, где находится программа, в конце строки должен стоять 0.

ES:BX   адрес блока параметров (см. ниже) и выполнить программу, 3-загрузить оверлей.

Если после выполнения данной функции С будет восстановлен, в регистре АХ будет содержаться код ошибки. По поводу загрузки оверлеев может сразу возник­нуть вопрос: зачем нужна эта функция, если оверлей можно загрузить как обычный файл (что мы только что сделали)? Ответ очень прост: оверлей может иметь структуру Разбираться в заголовке и самому настраивать адреса Боже упаси! Рассмотрим структуру блока параметров. Вначале разберем блок параметров для за­пуска программы (Рис. 11.3).

Смещение

Длина

Содержимое

О

2

Сегмент окружения. Если 0, то задача наследует окружение родителя. Можно придумать свое собственное окружение.

2

4

Адрес командной строки для запускаемой программы. Данная строка затем помещается по адресу 80Н в Р5Р. В начале строки должен стоять байт длина. Строка должна заканчиваться кодом 13.

б

4

Адрес блока РСВ для помещения в Р8Р по адресу 5СН.

i0

4

Адрес блока РСВ для помещения в Р8Р по адресу 6СН.

14

i

Длина блока параметров.

Pud. 11.3. Блокпараметровдлязапуска программы.

ТСВ в 6-13 байтах блока параметров должны содержать информацию о файлах, указанных в командной строке. Если таковых нет, или если программа не работает с РСВ, то указанные байты заполняются нулями.

Ниже (Рис. 11.4) представлен блок параметров для загрузки оверлея.

Смещение Длина Содержимое

С) 2    Сегмент, в который будет загружен файл.

2 4    Фактор перемещения, для корректировки сегментных ссылок.

6 1     Длина блока параметров.

Рис. 11.4. Блок параметров для загрузки оверлея.

Вы можете загрузить оверлейкакво внутренний сегмент, теки вне программы. Если Вы загружаете оверлей вне программы, то сначала освободите для этого место. Фактор привязки дает смещение для настройки адресов (например, в командах MOV BX,SEG MEM, длинный переход идр.). Помещайте в фактор привязки сегментный адрес оверлея.

DATA SEGMENT

F NAME    DB  'С:\DOS\FORMAT.COM',О

PARAM

DW

0

 

 

DW

OFFSET STR

;указываем на строку параметров

 

DW

SEG STR

 

FCl

DW

OFFSET FCBl

/указываем на первый FCB

 

DW

SEG FCBl

 

FC2

DW

OFFSET FCB2

/указываем на второй FCB

 

DW

SEG FCB2

 

FCBl

DB

40 DUP(O)

 

FCB2

DB

40 DUP(O)

 

STR

DB

2,'A:',13

/ строка параметров для программы

_SS

DW

?

 

_SP

DW

?

 

TEXT

DB

закончила свою

DATA

 

ENDS

 

SSEG

 

STACK

 

 

DB

50 DUP(?>

 

SSEG

 

ENDS

 

CODE

 

SEGMENT

 

ASSUME

BEGIN:

MOV

MOV DS,AX

. MOV ВХ, SEG ZSEG

MOV AX,ES ;ES указывает на начало PSP

SUB ВХ,АХ MOV АН,4АН

INT 21H ;обрезаем программу

/--указываем на   блок параметров

MOV AX,SEG PARAM

MOV ES,AX

LEA  BX, PARAM /--сохранить  копии   стековых регистров

MOV _SS,SS

MOV _SP,SP

на строку имени

LEA DX,F_NAME /—загрузка и выполнение программы

MOV АХ,4В00Н

INT 21H

/ —восстанавливаем сегментные регистры MOV АХ,DATA MOV DS,AX

MOV SS,_SS MOV SP,_SP JC EXIT

/—печатаем строку,   если  все  прошло успешно LEA DX,TEXT MOV АН,9

INT 21H

EXIT:

MOV    АН,4CH

INT 21H

CODE ENDS ZSEG SEGMENT ZSEG ENDS

END BEGIN

Puc. 11.5. ПрограммныйзапускутилитыFORMAT.COM.

НаРис. 11.5 представлен пример запуска программы издругой программы. В каче­стве запускаемой программы выбрана известная утилита DOS FORMAT.COM. Обра­щаю Вашевниманиенато,чтовданномслучаенампонадобилосьрезервироватьместо для блоков FCB29. Крометого, необходимо указать строку параметров дляданной про­граммы, так как она помещается в PSP с адреса 80Н (в начале — длина, затем сами

« Использование ГСВдляработыс файлами -устаревший метод. Однако некоторые утилиты DOS пользуются этим методом (см.[5]).

параметры, в конце—0DH). Если бы программа не использовала FCB и не требовала быпараметров, то можно бьшоограничитьсявсегоодаойстрокой: PARAMDB 14DUP(0).

. V.

Перейдемкрассмотрению использования функции Ш84ВНдля загрузки оверлея. На Рис. 1 Ебприведенапрограмма, которая будетигратьрольоверлея. Впринципе, это обычная ЕХЕ-программа. Есть, однако, небольшой нюанс: выход из нее осуществляется по RETF. Если запустить программу из операционной системы, то, выполнив предназначенные для неедействия, онанесможетвыйти обратно - "зависнет". Второймомент, на который я хочу обратить Ваше внимание, этото, что в оверлее определен стек. Однако при вызове оверлея регистры SS и SP указывают на стек основной программы, в оверлее же мы не меняем их значения. Поэтому в принципе свойстекв оверлее не нужен. А вот если бымыне направили регистр DS на сегмент данных, то он бы показывал на сегмент данных основной програм­мы, и желательного эффекта мы бы не добились. Наконец, последнее, что я должен Вам сообщить по программам на Рис. 11.6-11.7. Обе программы необходимо транслировать с ошшей/а.Дляссновнойпрофаммыэтонеобходи^^

а для оверлея такая необходимость связана с тем, что в этом случая первым будет идти сег­мент кода, и адрес OVR_SEG:OVR_OFFb основной программе будет какраз указывать на начало программы, т.е. BEGIN30. Попробуйте оттранслировать оверлей без указанной оп­ции, поставив сегментданных впереди, и оверлей перестанет запускаться.

DATA SEGMENT

TEXT DB   'Привет!',13,10,'$'

DATA ENDS

SSEG  SEGMENT STACK DB  50 DUP(?)

SSEG ENDS

CODE SEGMENT

ASSUME CSrCODE,   DS:DATA, SSrSSEG

BEGIN:

MOV AX,DATA MOV DS,AX LEA DX,TEXT

MOV AH, 9

INT 21H

RETF ;возврат из оверлея

CODE ENDS

END BEGIN

Рис. 11.6. Программа-оверлей, загружаемая из программы на Рис. 11.7.

Это будет выполняться, если тип подгонки сегмента кода РАИА(см. главу 13), что устанав­ливается по умолчанию.

DATA  SEGMENT ' ' !'....■■

PARAM     DB 4 DOP(O) PATH DB   ' PRl.EXE',0 OVR_OFF        DW 0 OVR_SEG     DW ?

STR_ER DB 'Шибка при загрузке оверлея.', 13,10, '$' STR DB   'Оверлей закончил свою работу.',13,10, '$'

DATA    ENDS ,■ ■

SSEG SEGMENT STACK

DB  50 DUP(?)

SSEG ENDS

CODE SEGMENT

ASSUME CS:CODE,   DS: DATA,   SS-.SSEG,   ES: DATA

BEGIN:

MOV AX,DATA .  m      DS, AX

MOV BX,SEG   ZSEG . .        ' ■ '. ■

MOV AX,ES ; SUB BX,AX . MOV AH,4AH

INT 21H

/выделяем для программы 2000 байт MOV ВХ,2000

MOV

INT 21Н JC ERR

MOV

регистры для

указывает на начало PSP

программу

;если не хватает памяти,   то выход ■

загрузки

MOV

АХ,DATA

MOV

ES,AX

LEA

ВХ,PARAM

MOV

AX,OVR_SEG

MOV

[ВХ],АХ

MOV

[ВХ]+2,АХ

LEA

DX,PATH

MOV

AL, 3

MOV

АН,4ВН

INT

21H

jc

err .

CALL

DWORD PTR

MOV

АХ, DATA

MOV

DS,AX

куда загружается оверлей привязки совпадает с сегментом

оверлея

MOV

INT

DX,STR AH, 9

21H

EXIT:

MOV INT

АН,4CH

21H

ERR:

LEA MOV INT JMP

DX,STR ER

CODE

ZSEG ZSEG

AH, 9

21H

EXIT ENDS

SEGMENT ENDS

END BEGIN

Рис. 11.7. Программа, загружающая оверлей (см. Рис. 11.6).

До сих пор мы говорили о "легальной" работе с памятью - посредством функций DOS. В главе 2 было дано понятие блоков управления памятью. Это недокументиро­ванные сведения. Однако, судя по всем признакам, такая структура памяти останется . в силе и в дальнейшем. Управлять памятью (занимать память, освобождать память и т.д.) можно, непосредственно работая с этими блоками. И хотя это гораздо сложнее, есть ситуации, когда такой подход необходим. В следующих главах будут даны конк­ретные примеры (см. также раздел VII данной главы).

Наконецпришловремяпоговоритьозавершениипрограммы.Функция4СНявля-ется универсальной. Ею можно завершать как СОМ, так и ЕХЕ-программы. Кроме того, она позволяет возвращать код выхода родительскому процессу. Однако есть бо­лее старые и традиционные способы завершения программ.

В первых двух байтах PSP стоит команда INT 20Н. Это традиционная команда завершения программы. Однако, для того чтобы эта команда сработала, необходимо, чтобы регистр CS указывал на начало PSP. Для СОМ-программыэтотак, и завершать ее можно, выдав команду INT20H. Для ЕХЕ-программы это обычно не так, и, чтобы выйти из положения, можно сделать длинный JMP (JMP FAR) на начало PSP. При запуске ЕХЕ-программы DS и ES указывают на PSP, поэтому проблем в нахождении этого сегмента не существует. Подготовьте в сегменте данных четыре байта:

PSP_OFF DW 0 ; смещение 0 в сегменте PSP

PSP SEG DW ?

VI.

В начале программы выполните команду MOVPSP_JSEG.ES указывает уже на сегмент данных). В конце же программы выполните команду:

JMP DWORD PTR

и программа будет завершена. Замечу, что после запуска программы в стек поме­щается слово 0. Это означает, что СОМ-программу можно завершить просто коман­дой RET (!).

Наконец, еще есть функция DOS 0, которая фактически идентична прерыванию 20Н. На адрес возврата из программы направлен также вектор 22Н.

О том, как завершить программу и оставить ее при этом в памяти (резидентно), мы поговорим в следующей главе.

VII.

В главе 2 мы кратко коснулись структуры управляющего блока памяти МСВ. В данной главе приводится полезная и простая программа, с помощью которой можно

провести исследование основной памяти на предмет управляющих блоков. DATA SEGMENT

STRO        DB 'конецпамяти ' , 13,10, * $ '

STR1 DB ' Сегмент блока 1 , 13,10, '$ '

STR2        DB 'Длина блока в параграфах1 , 13,10, '$'

STR3        DB 'Свободный',13,10, '$'

STR4 DB 'Параграф владельца' , 13,10, '$ '

STR5 DB   '--,\И,\Ъ/ $%

STR6        DB 'Имя владельца' , 13,10, '$' STR7        DB 'Адрес последнего параграфа '  ,13,10, '$' /строка для  вывода   шестнадцатеричного числа STROKA     DB '0000',13,10, '$' /шаблон

SHABL DB    'О','Г, '2','3','4', 'SVeVTVeVSVAVBV'CVDVEVF'

DATA ENDS

стека

STECK SEGMENT STACK

DB   100 DUP(?)

STECK ENDS

кода

CODE SEGMENT

ASSUME CS:CODE,   DS:DATA,   SS:STECK

BEG:

MOV   AX, DATA

MOV    DS, AX

CALL BEG_MEM

PROD:

адрес

LEA     DX,STR1 ■ ■ .....

CALL TEXT

MOV AX,ES

INC AX CALL HEX ;длина блока в параграфах LEA    DX, STR2 CALL TEXT MOV   AX,ES:[3]

CALL HEX ;параграф владельца LEA    DX, STR4

CALL TEXT

MOV AX,ES:[1]

CALL HEX

;имя владельца ■

LEA DX,STR6

CALL TEXT

CALL NAM LEA    DX, STR5

CALL TEXT ;следующий блок? CALL NEXT

CMP AL, 0 JZ PROD LEA DX,STR0

CALL TEXT

LEA    DX, STR7

CALL TEXT

MOV AX,ES ADD   AX,ES:[3] MOV ES,AX

CALL HEX

KONEC:

MOV АН,4CH

INT 21H ;раздел процедур ;адрес первого блока в ES BEG_MEM PROC

MOV АН,52Н

INT 21H

MOV ES,ES:[BX-2]

RET

BEG_MEM ENDP

;вывод шестнадцатеричного числа,   находящегося в АХ

HEX PROC

XOR DX,DX MOV BX,1000H

DIV

ВХ

MOV

ВХ,АХ

MOV

PTR

MOV

STROKA,BL

MOV

АХ, DX

XOR

DX, DX

MOV

ВХ,100Н

DIV

BX

MOV

ВХ,АХ

MOV

PTR

MOV

STR0KA+1, BL

MOV

AX,DX

XOR

DX,DX

MOV

ВХ, ЮН

DIV

BX

MOV

ВХ,АХ

MOV

PTR

MOV

STROKA+2,BL

MOV

ВХ, DX

MOV

PTR

MOV

STROKA+3,BL

LEA

DX,STROKA

CALL TEXT

RET

 

HEX

ENDP

перехода  к  следующему блоку ; адрес в ES,   если в AL 0,   если 1,   тогда блоков больше нет ;если 2,   то текущий блок памяти разрушен

NEXT

PROC

MOV AL,1

CMP BYTE  PTR ES:

JZ NET

CMP BYTE   PTR ES: JZ NET1 MOV AL,2

JMP SHORT NET

[0],'Z' [0], 'M'

NET1

NET:

NEXT

7-4072

MOV INC AX

ADD AX,ES: MOV ES,AX XOR AL,AL

RET

ENDP

[3]

;имя блока NAM PROC

CMP WORD PTR ES: [1] , О

JZ    WWWW ;не свободный ли блок?

PUSH ES

MOV DI,ES .

MOV  SI,ES:[1]

DEC SI

CMP SI,DI

JZ WO

MOV ES,SI

WO:

XOR BX,BX

MOV AH, 2

WW:

MOV  DL,ES:[BX][8] CMP DL,65

JB WWW INT 21H INC BX

JMP  SHORT WW

WWW:

MOV DL, 13

INT 21H MOV DL,10

INT 21H POP ES RET

WWWW:

LEA DX,STR3

CALL TEXT RET

NAM ENDP

;вывод строки

TEXT PROC

MOV AH, 9

INT 21H RET

TEXT ENDP

CODE ENDS END BEG

Рис. 11.8. Программа исследования блоковой структуры основной памяти.

Данную программу- назовем ее MEMO - удобно использовать, перенаправляя вы­вод вфайл. Например, MEMO > MEMO.LST. Обращаю Ваше внимание нато, какпрограмме находится адрес заголовка первого управляющего блока. Функция 52Н возвращает указатель на блок DOS'obckhx переменных, который помещается в паре регистров ES:BX. В частности, там содержится и необходимый нам адрес. Обращаю также внимание и на то, что процедура NEXT может возвращать в AL и 2, что означа­ет ошибку в структуре блока.

VIII.

Зададимся теперь одним интересным вопросом: можно ли использовать PSP в своих целях, например, для хранения данных или кода программы? Это может иметь смысл, например, для резидентньгх программ (см. главу 12). Вообще говоря, портить PSP не­желательно: там хранятся важные данные, в частности, необходимые для правильного завершения программы. Сейчас будет показано, как можно справиться с этими труд­ностями.

CODE SEGMENT

ASSUME CS:CODE,  DS:CODE,   SS:CODE, ES:CODE ORG 10OH

BEGIN:

;найти сегментный адрес ;вначале размер программы в параграфах mov ВХ,1б

mov АХ,OFFSET privet XOR DX,DX DIV BX

INC AX

mov BX,AX  ;для функции 4АН

/уменьшить размер  выделенного пространства mov АН,4АН

INT 21H  ; значение ES совпадает с CS для СОМ-программы ; найти место  для PSP

mov ВХ,16

mov АН,48Н

INT 21H

mov ES,AX /пересылка psp

mov si,o

mov DI,0 mov CX,100H

CLD

REP MOVSB /установка нового PSP mov АН,50H mov BX,ES

INT 21H

T

/"испортим" старый PSP

MOV WORD PTR CS:[0],0 MOV WORD   PTR CS:[0AH],0 MOV WORD  PTR CS:[0AH+2],0

;все освободить самому MOV АН,4 9H

INT 21H

MOV ES,ES:[2CH] MOV AH,49H

INT 21H PUSH CS POP ES

MOV

INT 21H

;выход

MOV АН,4CH

INT 21H PRIVET: CODE ENDS

END BEGIN

Рис. 11.9. Пример программы, использующей новый PSP.

Чтобы понять суть проблемы, Вам необходимо поэкспериментировать с этой про­граммой. Сделаю несколько пояснений. Если испортить некоторые байты в PSP (см. текст программы), то будет невозможно выйти в DOS даже с помощью функции 4СН. Поэтому перед тем, как испортить PSP, мы переносим его в другое место и при помо­щи функции 50Н сообщаем DOS, что у нас теперь новый PSP. Попробуйте убрать вызов данной функции, и программа перестанет выходить в DOS. Есть еде одна осо­бенность: все занятые блоки памяти приходится освобождать самостоятельно, так как функция 4СН перестает справляться со всеми своими обязанностями. Последнее лег­ко объясняется: освобождение занятой памяти начинается с PSP.