ASCLZPAK, MSP1.6, Lazy Pack
ASC Screen Crusher
Один из первых серьёзных экранных паке-
ров, ASCLZPAK (1991) определил основные
идеи структуры большинства последующих фо-
рматов и соответствующих им депакеров.
А именно, хотя данные в файле пока и
лежат только байтами,но впервые реализова-
на довольно оптимальная последовательность
просмотра экрана - ломаными столбцами. При
этом экран логически разбивается на 3 тре-
ти и атрибуты,где атрибуты линейны,а трети
состоят из столбцов байт. Этот способ даёт
возможность сжатия незначительно хуже спо-
соба с полными столбцами, зато здесь проще
пересчёт линейной модели в реальный экран.
Познакомим читателя с идеей линейной
модели ZX экрана.
Заключается она в том,что каждому адре-
су на экране взаимно однозначно соответст-
вует виртуальный адрес в линейной модели,
причём последовательность адресов экрана,
порождаемая порядком просмотра (в данном
случае - ломаными столбцами) выглядит на
линейной модели последовательностью подряд
идущих виртуальных адресов. Т.е.при после-
довательном переборе виртуальных адресов
линейной модели - адреса на реальном экра-
не будут перебираться ломаными столбцами.
Разумеется, для этого нужно использовать
процедуры пересчета виртуальных адресов в
реальные.
Как правило, начальные адреса реального
и виртуального экранов совпадают, чтобы
адреса атрибутов не пересчитывались.
Вот как выглядит пересчёт экранного ад-
реса из виртуального в реальный по ASC'у
(страшновато,но примите во внимание,что он
первый!):
──────────────────────────────────────────
LD A,H
AND #58
CP #58
JR Z,atr
LD C,A
LD A,L
AND 7
OR C
LD C,A
ADD HL,HL
ADD HL,HL
LD A,H
AND #1F
LD H,A
LD A,L
AND #E0
OR H
LD L,A
LD H,C
atr
──────────────────────────────────────────
Распаковщик ASCLZPAK, согласно тради-
ции,перемещаем и для этого настраивает три
пары ячеек программы под текущий адрес.
Опасного использования стека нет.
Перемещение по экрану ведётся в реаль-
ных адресах, и для координации этого (нес-
тандартное решение) применено 3 регистра-
счётчика:
A' = 8·номер столбца + 3-номер трети;
B' = строка внутри знакоместа;
C' = номер ряда знакомест внутри трети.
Упакованные данные располагаются непос-
редственно после программы.Формат простой:
%0bbbbHHH,LLLLLLLL - ссылка назад, disp=
=HHHLLLLLLLL, puts=bbbb+3
%10000000 - конец файла
%10bbbbbb,байты - bbbbbb байт взять из
потока
%11bbbbbb,байт - повторить байт bbbbbb+3
раз (можно повторить байт до 66 раз, т.е.
больше, чем через ссылку)
Maxsoft Screen Packer 1.6
Упаковщик Maxsoft Screen Packer 1.6
поддерживает 3 режима распаковки и в связи
с этим 3 различных распаковщика:
1. Непосредственно на экран, ломаными
столбцами;
2. Через буфер ломаными столбцами;
3. Непосредственно на экран в экранной
структуре.
Последний вариант наиболее прост, но
имеет,что понятно, худшее качество сжатия.
Распаковка же через буфер ничем принципиа-
льно не отличается от распаковки на экран.
Поэтому ниже будет рассматриваться именно
режим распаковки на экран (1) - RTD в
терминологии автора программы (RealTime
Depack). Следует отметить, что распаковщик
MSP1.6 является наиболее запутанным экран-
ным распаковщиком из всех, с которыми мы
будем иметь дело. Если же сравнивать еще и
с кодовыми, то рядом можно поставить разве
что deHrust 1.x.
В упакованных данных имеется два потока
- битовый и байтовый. Выборка битов из по-
тока производится парами байт (ADD HL,HL:
DJNZ:POP HL:LD B,C), c использованием двух
дополнительных регистров: B - счетчик, C -
константа 16. Начало байтового потока (DEC
SP:POP AF) приходится на третий байт фай-
ла,после двух первых байт битового потока.
В дальнейшем потоки чередуются в зависимо-
сти от структуры файла.Битовый поток - уп-
равляющий.
Для перемещаемости депакера указатель
основного стека сохраняется в регистре
IY, а не в памяти. Также две смежных яче-
йки программы подстраиваются под адрес её
расположения:
──────────────────────────────────────────
DI
CALL #52
DEC SP,SP
POP DE
LD HL,#110
ADD HL,DE
EX DE,HL
LD BC,#98 ;адрес ldto
ADD HL,BC ;(см.ниже)
LD (HL),E
INC HL
LD (HL),D
──────────────────────────────────────────
Таким образом реализуется подобие вызо-
ва подпрограммы nxtde, которая, как вполне
понятно, делает обычную операцию перевода
указателя DE на следующий байт экрана по
структуре ломаных столбцов:
──────────────────────────────────────────
nxtde INC DE
LD A,D
CP #58
JR NC,atr
DEC DE
INC D
LD A,D
AND 7
JR NZ,atr
LD A,E
ADD A,#20
LD E,A
JR NC,sub8
INC E
BIT 5,E
RES 5,E
JR NZ,atr
sub8 LD A,D
SUB 8
LD D,A
atr JR ... ;смещение в этом JR
;может принимать 2 разл.знач.
──────────────────────────────────────────
(атрибуты - линейно).
Вызов происходит так:
──────────────────────────────────────────
LD A,#8B ;смещение exjr
ldto=$+1
ldjr LD (atr+1),A
JR nxtde
exjr EX DE,HL
LD A,#90 ;смещение okjr
JR ldjr
okjr EX DE,HL
──────────────────────────────────────────
Указанный фрагмент всегда проходится
насквозь, то есть решалась задача именно
смещения ОБОИХ указателей - HL и DE (вход-
ного и выходного при копировании, соответ-
ственно).Это связано с тем,что перемещение
по экрану ведётся в реальных адресах,как в
ASCLZPAK.
Заголовка у файла нет, следует он непо-
средствено за распаковщиком. Первый байт
байтового потока (3-й байт файла) является
первым байтом экрана,далее по битовому по-
току:
%0 - просто байт (из байтового потока)
%100 - puts=3
%1010 - puts=2 (обратите внимание, код
длиннее предыдущего. Это должно означать,
что ссылки длиной 2 встречаются реже ссы-
лок с длиной 3 )
%1011,байт: -1 =конец файла, -2 =16-битное
значение puts (POP BC), иначе puts=байт+
+10 (puts=10..263)
%1100 - puts=4
%1101 - puts=5
%11100 - puts=6
%11101 - puts=7
%11110 - puts=8
%11111 - puts=9
Если puts не равен 2, то производится
декодирование старшего байта смещения (по-
ложительного disp):
%1 - 0
%0000 - 1
%0001 - 2
%00100 - 3
%00101 - 4
%00110 - 5
%00111 - 6
%010000 - 7
%010001 - 8
%010010 - 9
%010011 - 10
%010100 - 11
%010101 - 12
%010110 - 13
%0101110 - 14
%0101111 - 15
%0110000 - 16
%0110001 - 17
%0110010 - 18
%0110011 - 19
%0110100 - 20
%0110101 - 21
%0110110 - 22
%0110111 - 23
%0111000 - 24
%0111001 - 25
%0111010 - 26
%0111011 - 27
%0111100 - 28
%0111101 - 29
%0111110 - 30
%0111111 - 31
(ссылки с длиной 2 все короткие, т.е.
dispH=0 )
Коды dispH>27 нельзя использовать в эк-
ране,и это придаёт формату некоторую неже-
лательную избыточность.
Далее байт из байтового потока опреде-
ляет младший байт смещения. Вычитание сме-
щения из текущего адреса в виртуальном эк-
ране ( IX адресует его в линейной модели)
реализовано достаточно остроумно:
──────────────────────────────────────────
DEC SP
POP AF
SUB LX
CPL
CCF
LD L,A
LD A,HX
SBC A,H
LD H,A
──────────────────────────────────────────
После этого виртуальный адрес в HL пе-
ресчитывается в экран и происходит копиро-
вание блока. Затем всё описанное повторяе-
тся и т.д.
Lazy Pack 2.0
К сожалению, этим упаковщиком экранов
могут воспользоваться не все - по непонят-
ным причинам на большинстве версий TR-DOS
программа при сохранении уничтожает ката-
лог диска. Но формат заслуживает рассмот-
рения.
В формате использованы почти все хоро-
шие идеи по сжатию экранов, какие только
реализованы в LC5.2, но по длине распако-
вщика Lazy 2.0, безусловно, проигрывает LC
5.2.
Имеются битовый и байтовый потоки в
простейшем варианте: битовый поток извле-
кается побайтно в регистр D', B'=счётчик,
C'=константа 8 для помещения в B'. Первый
байт файла принадлежит битовому потоку.
Виртуальный адрес экрана в линейной мо-
дели содержится в IX, реальный адрес - в
DE.
Распаковщик не привязан к экрану, вызы-
вается с параметром: HL=адрес упакованного
экрана. Не использует стек (входные данные
в HL' ). Распаковывает на экран #4000, но
это число легко исправить на любое кратное
#2000. Если немного изменить код, то и на
любое кратное #800. Распаковщик достигает
перемещаемости, организуя в области MEMBOT
системных переменных бейсика три JP на три
внутренние подпрограммы распаковщика,усло-
вно назовём их get,down и bit. Определение
своего адреса распаковщик делает через DEC
SP,SP, поэтому нужно проследить,чтобы пре-
рывания были выключены.
get (+#aa, #5c97) - извлекает из бито-
вого потока в аккумулятор число 0...23 по
следующему дереву:
%1 - 0
%01 - 1
%0010 - 2
%0011 - 3
%000100 - 4
%000101 - 5
%000110 - 6
%000111 - 7
%00001000 - 8
%00001001 - 9
%00001010 - 10
%00001011 - 11
%00001100 - 12
%00001101 - 13
%00001110 - 14
%00001111 - 15
%00000000 - 16
%00000001 - 17
%00000010 - 18
%00000011 - 19
%00000100 - 20
%00000101 - 21
%00000110 - 22
%00000111 - 23
down (+#cc,#5c9a) - пересчитать адрес в
DE, чтобы он соответствовал следующему
байту на экране (в порядке ломаных столб-
цов).
bit (+#ea, #5c94) - взять один бит из
битового потока (результат во флаге пере-
носа).
Формат:
%1 - просто взять байт (из байтового пото-
ка);
%0 - вызываем get.
Если результат =7:
Читаем некий byte из байтового потока.
Если он не -1, то puts=byte+25, иначе
вызываем get (putsH), потом извлекаем из
байтового потока putsL.
Если результат <7:
Прибавляем 2, получаем puts=2..8.
Если результат >7:
Прибавляем 1, получаем puts=9..24.
Далее вызываем get (dispH) и берём байт
dispL из байтового потока.Реализуем полу-
ченную ссылку и т.д.
Специального кода выхода из распаковщи-
ка не предусмотрено. Распаковщик сам конт-
ролирует окончание экрана.
подготовил А. Кодер