Многие программисты пользуются программой PKLite фирмы PKWare. Эта
программа сжимает EXE- и COM-файлы. При сжатии этих файлов PKLITE'ом
их объём уменьшается, а работают они так же, как и раньше. Разжимает
программу в памяти PKLITE практически мгновенно.
А нельзя ли использовать эту удобную утилиту для уменьшения объёма
файлов, содержащих данные? Конечно, при этом можно сжимать не любые
данные, а только те, которые будут использоваться в ВАШЕЙ программе.
Сделать это очень просто. Для этого нужно дописать в начало данных
маленький блок, сжать данные PKLITE'ом, а затем из главной программы
с помощью процедуры EXEC загрузить и выполнить данные. Блок кода,
который дописывается в начало данных, узнаёт, в какой точке находятся
загруженные данные, и выходит в родительскую программу.
Вот пример кода, дописываемого в начало файла для размера данных
не более 64 K (создание COM-файла):
xor ax, ax
mov ds, ax
mov word ptr ds:[7Ch], cs
ret
В шестнадцатеричном коде это выглядит так: 33, C0, 8E, D8, 8C, 0E, 7C, 00, C3.
Как видите, при запуске и выполнении этого файла с данными он запишет
по адресу 0000:007C значение сегмента кода. После возврата в главную
программу будет известно, где начинаются загруженные данные. Адрес
0000:007C - это вектор прерывания графических символов 1Fh. Мы посылаем CS
туда потому, что переменные родительской программы будут недоступны.
Такой способ сжатия очень удобен для загрузки картинок и оверлеев. При
этом программисту не нужно писать свой упаковщик. Я, например, успешно
применяю PKLITE для сжатия картинок в играх.
Но и у этого способа есть два основных недостатка. Первый: нужно всегда
оставлять немного свободной памяти, необходимой для разворачивания файла.
Второй: все сжатые файлы с данными должны создаваться во время написания
главной программы.
Вот программа, которая демонстрирует запуск и вывод на экран картинки
для CGA адаптера размером 16000 байт:
{$M 1024,0,0} {Heap - 0 байт. Всегда должно быть достаточно памяти,
чтобы запустить и распаковать картинку.}
uses Dos;
var
OldVec, w: Word;
Begin
OldVec:=memW[0,$7C]; {Запоминаем старый вектор 1Fh}
Exec('SCREEN'); {"Запуск" и разжатие картинки}
asm
mov ax, 4 {Переход в графический CGA режим}
int 10h
end;
w:=memW[0:$7C]+$10; {Узнаём сегмент кода
загруженной программы + размер PSP}
move(mem[w:9], mem[$B800:0], 8000); {Вывод картинки}
move(mem[w:8009], mem[$BA00:0], 8000); {...}
asm
xor ax, ax {Ожидание нажатия клавиши}
int 16h
mov ax, 3 {Возврат в текстовый режим}
int 10h
end;
memW[0:$7C]:=OldVec {Возвращаем старое значение вектора 1Fh}
End.