§ Описание

Ниже представлен код для ассемблера fasm, который распаковывает данные, сжатые через утилиту compress. Она в деталях немного отличается от того, что сжимает GIF. Размер словаря может быть до 256 Кб. Необходимо указать следующие параметры:
  • EBX — стартовый адрес байтовой последовательности (без заголовка LZW)
  • ECX — размер входного файла
  • EBP — адрес начала словаря, временная память
  • EDI — область памяти, куда распаковать
При указании ebx нужно обязательно пропустить 3 первых байта заголовка, где написано LZW.

§ Код на ассемблере

LZW_decompress:

        mov     [.size], ecx        ; Окончание потока
        add     [.size], ebx
        call    .init
.rept:  cmp     ebx, [.size]
        jnb     .end                ; Декомпрессия закончилась?
        mov     cl, [.bitn]
        mov     eax, [ebx]
        shr     eax, cl             ; Сдвинуть на CL бит
        and     eax, [.mask]        ; Срезать bitn бит
        add     cl, [.bitk]         ; Передвинуть поток
        mov     [.bitn], cl
        and     [.bitn], byte 7     ; Запись в .bitn младших 3 бит
        movzx   ecx, cl
        shr     ecx, 3
        add     ebx, ecx            ; Передвинуть EBX на (CL>>3)
        cmp     eax, 100h
        jb      .single             ; ax < 100h -- простой байт
        je      .cmd                ; ax = 100h -- команда очистки словаря
        sub     eax, 101h           ; Указатель на построенный словарь
        cmp     edx, eax
        jbe     .error              ; eax - указатель на словарь если edx <= eax, словарь превышен

        ; СЛОВАРЬ (8 байт на 1 эл-т)
        ; 4 | +0 | Количество символов
        ; 4 | +4 | Указатель на строку для повторения
        mov     ecx, [ebp + 8*eax + 0]  ; Длина последовательности
        mov     esi, [ebp + 8*eax + 4]  ; Начало

        ; Скопировать кол-во символов из предыдущего элемента и +1 к длине
        ; Записать в следующий элемент текущий указатель EDI (построение словаря)
        lea     eax, [ecx + 1]
        mov     [ebp + 8*edx + 0], eax
        mov     [ebp + 8*edx + 4], edi
        rep     movsb
        jmp     .next

; Строительство нового словаря
.single:

        mov     [ebp + 8*edx + 0], dword 2  ; Добавить словарь: длина = 2
        mov     [ebp + 8*edx + 4], edi      ; Текущий указатель на выходной поток
        stosb   ; Скопировать один байт из входящего потока

.next:  ; Добавляем +1 элемент к словарю и переходим далее
        inc     edx
        cmp     edx, [.topnext]
        jb      .rept

        ; .topnext = [100, 300, 700, F00, 1F00, 3F00, 7F00]
        mov     eax, [.topnext]
        add     eax, 100h
        add     eax, eax
        sub     eax, 100h
        mov     [.topnext], eax
        inc     byte [.bitk]

        ; Установка новой маски (2^CH)-1
        mov     cl, [.bitk]
        mov     eax, 1
        shl     eax, cl
        dec     eax
        mov     [.mask], eax
        jmp     .rept

        ; Выровнять указатель
.cmd:   call    .init
        and     ebx, 0xFFFFFF0
        add     ebx, 10h
        jmp     .rept

        ; Сброс счетчиков и словаря
.init:  mov     [.bitn], byte 0
        mov     [.bitk], byte 9
        mov     [.mask], dword 1FFh
        mov     [.topnext], dword 100h
        xor     edx, edx
        ret

.error: stc ; СF=1 Ошибка декомпрессии
        ret
.end:   clc ; СF=0 Распаковалось успешно
        ret

; ----------------------------------------------------------------------
.bitn:       db 0            ; Текущий бит 0..7
.bitk:       db 9            ; Количество бит
.size:       dd 0            ; Размер входящего файла
.topnext:    dd 100h         ; Следующий верхний предел словаря
.mask:       dd 1FFh         ; Значение (1^.bitk)-1
; ----------------------------------------------------------------------