Из журнала Deja Vu #05, Кемерово, 01.07.98 (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, наверняка он покажет лучший результат, да и возможность сохранения файла без упа- ковщика предусмотрена автором.