SoundTrack: !-0 ;)-: BY SECTOR AFTER INSTY
__________________________________________
(C) Max/CYBERAX Software
Дополнение: dAn!!L
__________________________________________
-- -- --
Речь пойдет о паковщике MS-PACK, а точ-
нее о DEPACKER'е, который он присоединяет
к упакованным файлам.
В исходном виде UNPACKER MS-PACK'а дол-
жен работать при запрещенных прерываниях.
Также не предусмотрена возможность его от-
деления от блока (например, в вашей прог-
рамме много сжатых файлов и нет смысла
всем из них иметь распаковщик - достаточно
одного экземпляра).
Работа с включенными прерываниями при-
годится, если необходимо распаковать что-
либо,не тормозя при этом музыку или анима-
цию.В этом случае музыка вешается на IM 2,
а DEPACKER работает в основном режиме.
Для реализации всего вышеописанного и
предназначены две следующие программы.
Первая программа -на бейсике. Она пред-
назначена для отделения DEPACKER'a от
скомпрессированного блока. При запуске она
просит ввести имя файла (не более 7 букв).
Затем она записывает на диск блок без рас-
паковщика. К имени исходного файла добав-
ляется апостроф. Длина полученного блока
будет меньше, чем у блока с DEPACKER'ом на
247 байт.
1 INPUT "Name: ";A$ : RANDOMIZE USR VAL
"15619" : REM : LOAD A$ CODE
2 LET O=VAL"PEEK 23782 + 256*PEEK 23783":
LET A=VAL "PEEK (O+233) + 256*PEEK (O+
234)" : LET L=VAL"3 + PEEK (O+239) +
256*PEEK(O+240)"
3 FOR J=NOT PI TO VAL "4" : POKE VAL"O +
238 + J", PEEK VAL "A - 4 + J" : NEXT J
4 RANDOMIZE USR VAL "15619" : REM : SAVE
A$ + "'" CODE VAL "O + 238", L
Вторая программа - на ассемблере. Это и
есть, собственно, DEPACKER.
(Честно говоря,я так и не разобрался в ал-
горитме работы распаковщика. Я только за-
менил в нем все операции со стеком на опе-
рации с регистром IX, а также постарался
уместить новый вариант DEPACKER'а в 256
байт.)
ORG 40000
;+---------------------------------------+
;|*** MS-PACK UNPACKER WITH INTERRUPT ***|
;|---------------------------------------|
;| Originally written by 'MICROSPACE', |
;| remixed by Max 'CYBERAX Software' |
;+---------------------------------------+
UNPACK LD C,16
LD B,C
LD D,3
EXX
LD HL,SOURCE
;Адрес запакованного блока без UNPACKER'а.
;(Откуда распаковывать)
;Блок должен быть получен предыдущей прог-
;раммкой на бейсике.
LD DE,BUF
LD BC,5
LDIR
PUSH HL
POP IX
LD DE,DESTIN
;Адрес в памяти, куда будет производиться
;распаковка.
LD H,B
EXX
CALL POP_HL
JR ENTRY
FIRST CALL POP_AF
EXX
LD (DE),A
INC DE
EXX
ENTRY CALL NEWBIT
JR NC,FIRST
LD E,2
L1 XOR A
CALL NEWBIT
RLA
CALL NEWBIT
RLA
CP D
JR C,MET1
ADD A,E
LD E,A
CP 17
JR NZ,L1
XOR A
MET1 ADD A,E
CP 5
JR NC,MET2
CP 2
JR NZ,MET3
EXX
LD C,A
L3 CALL POP_AF
LD L,A
LD A,E
SCF
SBC A,L
LD L,A
LD A,D
SBC A,H
LD H,A
LDIR
LD H,B
EXX
JR ENTRY
L2 EXX
CALL POP_AF
INC A
JR Z,END
INC A
JR NZ,MET4
CALL POP_BC
JR MET5
MET4 ADD A,15
JR NC,MET6
INC B
JR MET6
MET2 JR Z,L2
DEC A
MET3 EXX
MET6 LD C,A
MET5 EXX
CALL NEWBIT
EXX
JR C,L3
EXX
XOR A
LD E,D
L4 CALL NEWBIT
RLA
DEC E
JR NZ,L4
CP 2
JR NC,MET7
INC A
LOOP EXX
LD H,A
JR L3
MET7 CALL NEWBIT
RLA
CP 8
JR NC,MET8
DEC A
JR LOOP
MET8 CALL NEWBIT
RLA
CP 23
JR NC,MET9
SUB 9
JR LOOP
MET9 CALL NEWBIT
RLA
AND 31
CP 31
JR C,LOOP
CALL POP_AF
JR LOOP
END LD HL,BUF
LD C,5
LDIR
;В случае,если вы будете использовать этот
;декомпрессор из бейсика, здесь желательно
;вставить команды восстановления регистра
;HL' (иначе бейсик сглючит...).
RET
NEWBIT ADD HL,HL
DJNZ MET
LD B,C
POP_HL LD L,(IX)
LD H,(IX+1)
FIN INC IX
INC IX
MET RET
POP_DE LD E,(IX)
LD D,(IX+1)
JR FIN
POP_BC LD C,(IX)
LD B,(IX+1)
JR FIN
POP_AF LD A,(IX)
JR FIN+2
BUF DEFS 5
SIZE EQU $-DEPACK
Адреса SOURCE и DESTIN выбираются не
произвольно. Вернее произвольно, но по оп-
ределенным правилам. Пусть длина исходного
блока (незапакованного) равна LEN1, а дли-
на упакованного (без UNPACKER'а) - LEN2.
Возможны два случая:
1) Допустим задан адрес DESTIN, тогда ад-
рес SOURCE может лежать в диапазоне от
16384 до DESTIN-LEN2 или в диапазоне от
DESTIN+LEN1-LEN2 до 65536-LEN2.
2) Можно зафиксировать адрес SOURCE, тогда
DESTIN может лежать в диапазоне от 16384
до SOURCE-LEN1+LEN2 или от SOURCE+LEN2 до
65536-LEN1.
Это справедливо при условии, что LEN1
больше LEN2 (т.е., если блок хоть немного,
но сжался), в противном случае эти формулы
не годятся. Более того,если от такого бло-
ка не отделять распаковщик, то последний
вообще не сможет распаковать этот блок
корректно (например, сбросится).
В заключении еще несколько слов.
Переделанный таким образом UNPACKER ра-
ботает несколько медленнее оригинала (это
еще без учета времени, занимаемого проце-
дурой обработки IM 2). Если нужна макси-
мальная скорость, можно поступить следую-
щим образом: запускать оригинальный UN-
PACKER при разрешенных прерываниях...
Для этого необходимо отделить процедуру
распаковки от блока и переделать ее начало
аналогично моему варианту.
При вызове нужно скорректировать пара-
метры SOURCE и DESTIN так,чтобы расстояние
между этими блоками увеличилось на столько
байт, сколько заносит на стек обработчик
IM 2 (или больше).
Немного поясню. Оригинальный DEPACKER
при запуске ставит стек на запаченный блок
и начинает его распаковывать в адрес DES-
TIN, при этом распакованный блок растет
вверх, а запакованный 'убегает' от него,
уменьшаясь в длине. Байты запакованного
блока снимаются командами POP. Самое инте-
ресное, что конец распакованного блока ни-
когда не 'догонит' начало распаковывающе-
гося, т.к. между ними оставлен зазор в 5
байт. Если мы искусственно увеличим этот
зазор (изменив SOURCE или DESTIN), то смо-
жем сохранять здесь регистры и адреса воз-
вратов при обработке IM 2.
Также отмечу, что второй способ я не
проверял, а вот первый успешно работает в
одной из моих программ.
* * *
В дополнение от редакции или как сдела-
но в DEJA VU.
Чтобы недолго мучаться и не задавать
вручную адреса и если вам не надо работать
с разрешенными прерываниями, то выполняем
следующие манипуляции:
1) Пакуем MS-PACK'ом кодовый файл и круто
тащимся от коэффициента компрессии (если
файлов несколько, то, конечно, пакуем их
все).
2) Запускаем следующую программку на
Basic'е:
10 CLEAR 25000
20 INPUT "MS:";N$
22 IF N$="" THEN GO SUB 100:GO TO 20
25 RANDOMIZE USR 15619:REM:LOAD N$ CODE
26 IF PEEK 23823 <> 0 THEN GO TO 20
28 LET LEN=PEEK 23784+256*PEEK 23785
30 LET AD=PEEK 23782+256*PEEK 23783:
LET AD2=AD+36:LET AD3=AD+231
40 POKE AD3,PEEK AD2: POKE AD3+1,
PEEK (AD2+1)
50 INPUT "NEW:";M$
60 IF M$="" THEN GO SUB 100:GO TO 50
70 RANDOMIZE USR 15619:REM:SAVE M$ CODE
AD3,LEN-231
80 GO TO 20
100 RANDOMIZE USR 15619:REM:CAT
110 RETURN
3) Вводим имя запкованного MS-PACK'ом
файла (строка 20), если такого файла нет,
то ничего не произойдет, в противном слу-
чае запросится имя нового файла; после
ввода имени запишется новый файл с адреса
СТАРЫЙ АДРЕС+231, длиной СТАРАЯ ДЛИНА-231,
т.е. на 231 байт короче!
4) Создаем универсальный депакер, ас-
семблируя в любимом ассемблере следующий
листинг:
ORG #5B00
LL5B00 LDDR ;либо LDIR см. ниже
LD H,B
POP DE
EXX
POP HL
JR LL5B0E
LL5B08 DEC SP
POP AF
EXX
LD (DE),A
INC DE
EXX
LL5B0E ADD HL,HL
DJNZ LL5B13
POP HL
LD B,C
LL5B13 JR NC,LL5B08
LD E,#02
LL5B17 XOR A
ADD HL,HL
DJNZ LL5B1D
POP HL
LD B,C
LL5B1D RLA
ADD HL,HL
DJNZ LL5B23
POP HL
LD B,C
LL5B23 RLA
CP D
JR C,LL5B2E
ADD A,E
LD E,A
CP #11
JR NZ,LL5B17
XOR A
LL5B2E ADD A,E
CP #05
JR NC,LL5B5C
CP #02
JR NZ,LL5B5F
EXX
LD C,A
LL5B39 DEC SP
POP AF
LD L,A
LD A,E
SCF
SBC A,L
LD L,A
LD A,D
SBC A,H
LD H,A
LDIR
LD H,B
EXX
JR LL5B0E
LL5B49 EXX
DEC SP
POP AF
INC A
JR Z,LL5BAA
INC A
JR NZ,LL5B55
POP BC
JR LL5B61
LL5B55 ADD A,#0F
JR NC,LL5B60
INC B
JR LL5B60
LL5B5C JR Z,LL5B49
DEC A
LL5B5F EXX
LL5B60 LD C,A
LL5B61 EXX
ADD HL,HL
DJNZ LL5B67
POP HL
LD B,C
LL5B67 EXX
JR C,LL5B39
EXX
XOR A
LD E,D
LL5B6D ADD HL,HL
DJNZ LL5B72
POP HL
LD B,C
LL5B72 RLA
DEC E
JR NZ,LL5B6D
CP #02
JR NC,LL5B7F
INC A
LL5B7B EXX
LD H,A
JR LL5B39
LL5B7F ADD HL,HL
DJNZ LL5B84
POP HL
LD B,C
LL5B84 RLA
CP #08
JR NC,LL5B8C
DEC A
JR LL5B7B
LL5B8C ADD HL,HL
DJNZ LL5B91
POP HL
LD B,C
LL5B91 RLA
CP #17
JR NC,LL5B9A
SUB #09
JR LL5B7B
LL5B9A ADD HL,HL
DJNZ LL5B9F
POP HL
LD B,C
LL5B9F RLA
AND #1F
CP #1F
JR C,LL5B7B
DEC SP
POP AF
JR LL5B7B
LL5BAA LD HL,LL5BBC
LD C,#05
LDIR
LD HL,#0000
LL5BB2 EQU $-#02
EXX
LD SP,#0000
LL5BB6 EQU $-#02
EI ; либо DI
RET ;
NOP ; либо JP #nnnn NOP ;/
LL5BBC NOP
NOP
NOP
NOP
LL5BC0 NOP
;запуск депакера на входе hl=адрес файла
;без депакера
DEPACK DI
LD (LL5BB6),SP
EXX
LD (LL5BB2),HL
LD C,#10
LD B,C
LD D,#03
EXX
LD SP,HL
POP HL
LD (LL5BE2),HL
POP HL
LD DE,LL5BC0
LD BC,#0005
LDDR
POP HL
POP DE
POP BC
LD SP,#0000
LL5BE2 EQU $-#02
JP LL5B00
Комментарии: универсальный распаковщик
занимает меньше 256 байт! Традиционно рас-
полагается в буфере принтера с адреса
#5B00. (в этом случае адрес запуска будет
равен #5BC1=метка DEPACK). На входе в пару
HL необходимо задать адрес загруженного
блока кодов без депакера (то, что получи-
лось в пункте 3). Чтобы избежать проблем с
наложением распаковывающихся данных на еще
не распакованные, советую грузить файл по
адресу, заданному в каталоге! Распаковка
будет происходить в тот адрес, который был
задан в MS-PACK'е.
Например, имеем запакованный MS-PACK'ом
файл:
"FILE" CODE 49152,10000;
Обработав его BASIC'ом (пункты 2 и 3)
получим новый файл без депакера:
"FILE+" CODE 49383,9769;
Чтобы распаковать данный файл, помещаем
распаковщик по адресу #5B00 и распаковыва-
ем следующим образом:
LD HL,49383
JP #5BC1 ;метка DEPACK
Файл распакуется по адресу заданному
при упаковке в MS-PACK'е (например #C000).
И, наконец, маленький нюанс - если при
упаковке файл получился больше исходного
(а такое может быть,если мы пакуем малень-
кий файл или уже запаченный файл - это
видно по коэффициенту компрессии заданно-
му в процентах), то необходимо изменить
депакер, по адресу #5B00 поместить не LDDR
а LDIR! Таким образом имеет смысл пако-
вать и маленькие файлы (2 сектора).
В итоге, если ориентироваться по коли-
честву секторов то в 90% случаев удается
сэкономить один сектор в файле!
Если использовать метод, который приво-
дит MAX, то в 96% случаев экономим один
сектор! А самое главное - есть возможность
выбора!
MS-PACK хорошо пакует небольшие текс-
товые файлы. Если вы пакуете большие ко-
довые блоки, то используйте HRUST 1.0,
наверняка он покажет лучший результат, да
и возможность сохранения файла без упа-
ковщика предусмотрена автором.
Дальше передаю слово MAX'у.
------------------------------------------
******************************************
------------------------------------------
Загрузка и запуск бейсик-файлов.
Думаю все знают, как в boot'ах обычно
запускаются программы. Имя файла кидается
в строку бейсика (вместо пробелов в коман-
де RUN " " ), затем эта строка за-
пускается.
В данной статье я расскажу, как можно
загрузить и запустить бейсик-программу без
использования подобного рода фокусов.
Программа в листинге написана с актив-
ным использованием фрагментов ПЗУ TR-DOS,
поэтому не удивляйтесь ее корявому виду.
Пусть есть конкретный файл,который нуж-
но запустить. Также допустим, что мы зна-
ем заголовок этого файла (16 байт). Для
загрузки будем использовать некий драйвер,
умеющий читать 'B' секторов в адрес 'HL' с
дорожки и сектора 'DE' (метка LOAD). Опос-
ля загрузки, драйвер должен заносить пос-
ледний трек и сектор в переменную #5CF4.
CLEAR желательно иметь установленным на
65367.
Первым делом перекинем заголовок в ад-
рес HEADER, равный 23773. Номер файла же-
лательно занести в #5D1E. Затем запускаем
следующую прогу:
BAS_RUN LD DE,(23635);начало бейс. пр.
LD HL,(23641);обл. ред. строки
DEC HL
PUSH HL
PUSH DE
AND A
SBC HL,DE
;в HL получили длину старой беисик-проги,
;т.е той, что находится (или не находится)
;в памяти до загрузки.
LD DE,(HEADER+9)
;в DE взяли из заголовка длину нового бей-
;сика, т.е. того, который нужно грузить.
PUSH DE
PUSH HL
LD HL,5
ADD HL,DE
;длину новой проги увеличили на 5 байт.
LD (23771),HL
;засунули в переменную, как в TR-DOS.
POP HL
POP BC
POP DE
POP HL
PUSH BC
CALL #19E5
;удаляем старый бейсик, сдвигая память.
POP BC
CALL #1655
;резервируем место для нового.
INC HL
LD BC,(HEADER+11)
;взяли из заголовка длину нового бейсика
;без программных переменных.
ADD HL,BC
;прибавили адрес начала.
LD (23627),HL
;установили системную переменную VARS.
LD HL,(23635)
;HL - куда будем грузить.
LD A,(23772)
;старший байт длины.
LD B,A
LD DE,(HEADER+14)
;DE - трек и сектор.
LD (#5CF4),DE
DEC B
INC B
CALL NZ,LOAD
;если есть что грузить - грузим.
LD A,(23771)
AND A
JR Z,NOLOW
;равен ли нулю младший байт длины?
PUSH HL
LD DE,(#5CF4)
LD HL,BUF
LD B,1
CALL LOAD
;грузанули последний сектор в буфер.
POP DE
LD BC,(23771)
LD B,0
LD HL,BUF
LDIR
;остаток перебросили куда надо.
NOLOW LD HL,(23641)
DEC HL
LD (HL),128
;занесли на всякий случай маркер конца.
INC HL
INC HL
LD E,(HL)
INC HL
LD D,(HL)
;взяли строку автостарта
LD (23618),DE
;и занесли ее в соотв. переменную.
XOR A
LD (23620),A
;занулили номер оператора в строке.
LD (23824),A
CALL #16B0
;точно не помню что такое, кажется очистка
;стека калькулятора.
LD HL,(23635)
DEC HL
LD (23639),HL
;сделали RESTORE.
LD HL,(23613)
LD (23827),HL
EXX
LD HL,10072
EXX
LD IY,#5C3A
LD (IY),255
SET 7,(IY+1)
RES 5,(IY+1)
;прочие установки.
JP 7037;запуск...
BUF DEFS 256;буфер загрузки.
Располагать сию программу лучше в эк-
ранной области. Неплохо бы почистить скрин
перед передачей управления в ПЗУ, как это
делает TR-DOS.
Например, вместо JP 7037:
LD HL,7037
PUSH HL
JP 3435
В заключении хочу выразить благодар-
ность Федину П.Ю. за книгу "Полное описа-
ние и полный дизассемблер TR-SOS 5.04T
(5.03)".