Contributor: ANDREW EIGUS

Unit LZSSUnit;
{
   LZSSUNIT - Compress and uncompress unit using LZ77 algorithm for
   Borland (Turbo) Pascal version 7.0.

   Assembler Programmer: Andy Tam, Pascal Conversion: Douglas Webb,
   Unit Conversion and Dynamic Memory Allocation: Andrew Eigus.

   Public Domain version 1.02, last changed on 30.11.94.
   Target platforms: DOS, DPMI, Windows.

   Written by Andrew Eigus (aka: Mr. Byte) of:
   Fidonet: 2:5100/33,
   Internet: aeigus@fgate.castle.riga.lv, aeigus@kristin.cclu.lv.
}

interface

{#Z+}
{ This unit is ready for use with Dj. Murdoch's ScanHelp utility which
  will make a Borland .TPH file for it. }
{#Z-}

const
  LZRWBufSize     = 8192; { Read buffer size }

{#Z+}
  N           = 4096;  { Bigger N -> Better compression on big files only. }
  F           = 18;
  Threshold   = 2;
  Nul         = N * 2;
  InBufPtr    : word = LZRWBufSize;
  InBufSize   : word = LZRWBufSize;
  OutBufPtr   : word = 0;
{#Z-}

type
{#X TWriteProc}{#X LZSquash}{#X LZUnsquash}

  TReadProc = function(var ReadBuf; var NumRead : word) : word;
  { This is declaration for custom read function. It should read
    #LZRWBufSize# bytes from ReadBuf. The return value is ignored. }

{#X TReadProc}{#X LZSquash}{#X LZUnsquash}
  TWriteProc = function(var WriteBuf; Count : word; var NumWritten : word) :
word;  { This is declaration for custom write function. It should write
    Count bytes into WriteBuf and return number of actual bytes written
    into NumWritten variable. The return value is ignored. }

{#Z+}
  PLZRWBuffer = ^TLZRWBuffer;
  TLZRWBuffer = array[0..LZRWBufSize - 1] of Byte; { file buffers }

  PLZTextBuf = ^TLZTextBuf;
  TLZTextBuf = array[0..N + F - 2] of Byte;

  PLeftMomTree = ^TLeftMomTree;
  TLeftMomTree = array[0..N] of Word;
  PRightTree = ^TRightTree;
  TRightTree = array[0..N + 256] of Word;

const
  LZSSMemRequired = SizeOf(TLZRWBuffer) * 2 +
    SizeOf(TLZTextBuf) + SizeOf(TLeftMomTree) * 2 + SizeOf(TRightTree);
{#Z-}

function LZInit : boolean;
{ This function should be called before any other compression routines
  from this unit - it allocates memory and initializes all internal
  variables required by compression procedures. If allocation fails,
  LZInit returns False, this means that there isn't enough memory for
  compression or decompression process. It returns True if initialization
  was successful. }
{#X LZDone}{#X LZSquash}{#X LZUnsquash}

procedure LZSquash(ReadProc : TReadProc; WriteProc : TWriteProc);
{ This procedure is used for compression. ReadProc specifies custom
  read function that reads data, and WriteProc specifies custom write
  function that writes compressed data. }
{#X LZUnsquash}{#X LZInit}{#X LZDone}

procedure LZUnSquash(ReadProc : TReadProc; WriteProc : TWriteProc);
{ This procedure is used for decompression. ReadProc specifies custom
  read function that reads compressed data, and WriteProc specifies
  custom write function that writes decompressed data. }
{#X LZSquash}{#X LZInit}{#X LZDone}

procedure LZDone;
{ This procedure should be called after you finished compression or
  decompression. It deallocates (frees) all memory allocated by LZInit.
  Note: You should always call LZDone after you finished using compression
  routines from this unit. }
{#X LZInit}{#X LZSquash}{#X LZUnsquash}

implementation

var
  Height, MatchPos, MatchLen, LastLen : word;
  TextBufP : PLZTextBuf;
  LeftP, MomP : PLeftMomTree;
  RightP : PRightTree;
  CodeBuf : array[0..16] of Byte;
  LZReadProc : TReadProc;
  LZWriteProc : TWriteProc;
  InBufP, OutBufP : PLZRWBuffer;
  Bytes : word;
  Initialized : boolean;

Function LZSS_Read : word;    { Returns # of bytes read }
Begin
  LZReadProc(InBufP^, Bytes);
  LZSS_Read := Bytes;
End; { LZSS_Read }

Function LZSS_Write : word;  { Returns # of bytes written }
Begin
  LZWriteProc(OutBufP^, OutBufPtr, Bytes);
  LZSS_Write := Bytes
End; { LZSS_Write }

Procedure Getc; assembler;
Asm
{
  getc : return a character from the buffer
          RETURN : AL = input char
                   Carry set when EOF
}
              push    bx
              mov     bx, inBufPtr
              cmp     bx, inBufSize
              jb      @getc1
              push    cx
              push    dx
              push    di
              push    si
              call    LZSS_Read
              pop     si
              pop     di
              pop     dx
              pop     cx
              mov     inBufSize, ax
              or      ax, ax
              jz      @getc2               { ; EOF }
              xor     bx, bx
  @getc1:
              PUSH    DI
              LES     DI,[InBufP]
              MOV     AL,BYTE PTR [ES:DI+BX]
              POP     DI
              inc     bx
              mov     inBufPtr, bx
              pop     bx
              clc                         { ; clear the carry flag }
              jmp     @end
  @getc2:     pop     bx
              stc                         { ; set carry to indicate EOF }
  @end:
End; { Getc }

Procedure Putc; assembler;
{
  putc : put a character into the output buffer
             Entry : AL = output char
}
Asm
              push    bx
              mov     bx, outBufPtr
              PUSH    DI
              LES     DI,[OutBufP]
              MOV     BYTE PTR [ES:DI+BX],AL
              POP     DI
              inc     bx
              cmp     bx, LZRWBufSize
              jb      @putc1
              mov     OutBufPtr,LZRWBufSize   { Just so the flush will work. }
              push    cx
              push    dx
              push    di
              push    si
              call    LZSS_Write
              pop     si
              pop     di
              pop     dx
              pop     cx
              xor     bx, bx
  @putc1:     mov     outBufPtr, bx
              pop     bx
End; { Putc }

Procedure InitTree; assembler;
{
  initTree : initialize all binary search trees.  There are 256 BST's, one
             for all strings started with a particular character.  The
             parent is tree K is the node N + K + 1 and it has only a
             right child
}
Asm
      cld
      push    ds
      pop     es
      LES     DI,[RightP]
{      mov     di,offset right}
      add     di, (N + 1) * 2
      mov     cx, 256
      mov     ax, NUL
      rep     stosw
      LES     DI,[MomP]
{      mov     di, offset mom}
      mov     cx, N
      rep     stosw
End; { InitTree }


Procedure Splay; assembler;
{
  splay : use splay tree operations to move the node to the 'top' of
           tree.  Note that it will not actual become the root of the tree
           because the root of each tree is a special node.  Instead, it
           will become the right child of this special node.

             ENTRY : di = the node to be rotated
}
Asm
  @Splay1:
              PUSH    BX
              LES     BX,[MomP]
              MOV     SI,[ES:BX+DI]
              POP     BX
{              mov     si, [Offset Mom + di]}
              cmp     si, NUL           { ; exit if its parent is a special
node }              ja      @Splay4
              PUSH    DI
              LES     DI,[MomP]
              ADD     DI,SI
              MOV     BX,[ES:DI]
{              mov     bx, [Offset Mom + si]}
              POP     DI
              cmp     bx, NUL           { ; check if its grandparent is special
}              jbe     @Splay5           { ; if not then skip }
              PUSH    BX
              LES     BX,[LeftP]
              CMP     DI,[ES:BX+SI]
              POP     BX
{              cmp     di, [Offset Left + si]} { ; is the current node is a
left child ? }              jne     @Splay2
              PUSH    BX
              LES     BX,[RightP]
              MOV     DX,[ES:BX+DI]
{              mov     dx, [Offset Right + di]}    { ; perform a left zig
operation }              LES     BX,[LeftP]
              MOV     [ES:BX+SI],DX
{              mov     [Offset Left + si], dx}
              LES     BX,[RightP]
              MOV     [ES:BX+DI],SI
              POP     BX
{              mov     [Offset Right + di], si}
              jmp     @Splay3
  @Splay2:
              PUSH    BX
              LES     BX,[LeftP]
              MOV     DX,[ES:BX+DI]
{              mov     dx, [Offset Left + di]}     { ; perform a right zig }
              LES     BX,[RightP]
              MOV     [ES:BX+SI],DX
{              mov     [Offset Right + si], dx}
              LES     BX,[LeftP]
              MOV     [ES:BX+DI],SI
              POP     BX
{              mov     [Offset Left + di], si}
  @Splay3:
              PUSH    SI
              LES     SI,[RightP]
              MOV     [ES:SI+BX],DI
              POP     SI
{              mov     [Offset Right + bx], di}
              xchg    bx, dx
              PUSH    AX
              MOV     AX,BX
              LES     BX,[MomP]
              ADD     BX,AX
              MOV     [ES:BX],SI
              LES     BX,[MomP]
              MOV     [ES:BX+SI],DI
              LES     BX,[MomP]
              MOV     [ES:BX+DI],DX
              MOV     BX,AX
              POP     AX
{              mov     [Offset Mom + bx], si
              mov     [Offset Mom + si], di
              mov     [Offset Mom + di], dx}
  @Splay4:    jmp     @end
  @Splay5:
              PUSH    DI
              LES     DI,[MomP]
              MOV     CX,[ES:DI+BX]
              POP     DI
{              mov     cx, [Offset Mom + bx]}
              PUSH    BX
              LES     BX,[LeftP]
              CMP     DI,[ES:BX+SI]
              POP     BX
{              cmp     di, [Offset Left + si]}
              jne     @Splay7
              PUSH    DI
              LES     DI,[LeftP]
              CMP     SI,[ES:DI+BX]
              POP     DI
{              cmp     si, [Offset Left + bx]}
              jne     @Splay6
              PUSH    AX
              MOV     AX,DI
              LES     DI,[RightP]
              ADD     DI,SI
              MOV     DX,[ES:DI]
{              mov     dx, [Offset Right + si]   } { ; perform a left zig-zig
operation }              LES     DI,[LeftP]
              MOV     [ES:DI+BX],DX
{              mov     [Offset Left + bx], dx}
              xchg    bx, dx
              LES     DI,[MomP]
              MOV     [ES:DI+BX],DX
{              mov     [Offset Mom + bx], dx}
              LES     DI,[RightP]
              ADD     DI,AX
              MOV     BX,[ES:DI]
{              mov     bx, [Offset Right + di]}
              LES     DI,[LeftP]
              ADD     DI,SI
              MOV     [ES:DI],BX
              LES     DI,[MomP]
              MOV     [ES:DI+BX],SI
{              mov     [Offset Left +si], bx
              mov     [Offset Mom + bx], si}
              mov     bx, dx
              LES     DI,[RightP]
              ADD     DI,SI
              MOV     [ES:DI],BX
              LES     DI,[RightP]
              ADD     DI,AX
              MOV     [ES:DI],SI
{              mov     [Offset Right + si], bx
              mov     [Offset Right + di], si}
              LES     DI,[MomP]
              MOV     [ES:DI+BX],SI
              LES     DI,[MomP]
              ADD     DI,SI
              STOSW
              MOV     DI,AX
              POP     AX
{              mov     [Offset Mom + bx], si
              mov     [Offset Mom + si], di}
              jmp     @Splay9
  @Splay6:
              PUSH    AX
              MOV     AX,SI
              LES     SI,[LeftP]
              ADD     SI,DI
              MOV     DX,[ES:SI]
{              mov     dx, [Offset Left + di]}     { ; perform a left zig-zag
operation }              LES     SI,[RightP]
              MOV     [ES:SI+BX],DX
{              mov     [Offset Right + bx], dx}
              xchg    bx, dx
              LES     SI,[MomP]
              MOV     [ES:SI+BX],DX
{              mov     [Offset Mom + bx], dx}
              LES     SI,[RightP]
              ADD     SI,DI
              MOV     BX,[ES:SI]
{              mov     bx, [Offset Right + di]}
              LES     SI,[LeftP]
              ADD     SI,AX
              MOV     [ES:SI],BX
{              mov     [Offset Left + si], bx}
              LES     SI,[MomP]
              MOV     [ES:SI+BX],AX
{              mov     [Offset Mom + bx], si}
              mov     bx, dx
              LES     SI,[LeftP]
              ADD     SI,DI
              MOV     [ES:SI],BX
{              mov     [Offset Left + di], bx}
              LES     SI,[RightP]
              ADD     SI,DI
              MOV     [ES:SI],AX
{              mov     [Offset Right + di], si}
              LES     SI,[MomP]
              ADD     SI,AX
              MOV     [ES:SI],DI
{              mov     [Offset Mom + si], di}
              LES     SI,[MomP]
              MOV     [ES:SI+BX],DI
              MOV     SI,AX
              POP     AX
{              mov     [Offset Mom + bx], di}
              jmp     @Splay9
  @Splay7:
              PUSH    DI
              LES     DI,[RightP]
              CMP     SI,[ES:DI+BX]
              POP     DI
{              cmp     si, [Offset Right + bx]}
              jne     @Splay8
              PUSH    AX
              MOV     AX,SI
              LES     SI,[LeftP]
              ADD     SI,AX
              MOV     DX,[ES:SI]
{              mov     dx, [Offset Left + si]}     { ; perform a right zig-zig
}              LES     SI,[RightP]
              MOV     [ES:SI+BX],DX
{              mov     [Offset Right + bx], dx}
              xchg    bx, dx
              LES     SI,[MomP]
              MOV     [ES:SI+BX],DX
{              mov     [Offset Mom + bx], dx}
              LES     SI,[LeftP]
              ADD     SI,DI
              MOV     BX,[ES:SI]
{              mov     bx, [Offset Left + di]}
              LES     SI,[RightP]
              ADD     SI,AX
              MOV     [ES:SI],BX
{              mov     [Offset Right + si], bx}
              LES     SI,[MomP]
              MOV     [ES:SI+BX],AX
{              mov     [Offset Mom + bx], si}
              mov     bx, dx
              LES     SI,[LeftP]
              ADD     SI,AX
              MOV     [ES:SI],BX
{              mov     [Offset Left + si], bx}
              LES     SI,[LeftP]
              ADD     SI,DI
              MOV     [ES:SI],AX
{              mov     [Offset Left + di], si}
              LES     SI,[MomP]
              MOV     [ES:SI+BX],AX
{              mov     [Offset Mom + bx], si}
              LES     SI,[MomP]
              ADD     SI,AX
              MOV     [ES:SI],DI
{              mov     [Offset Mom + si], di}
              MOV     SI,AX
              POP     AX
              jmp     @Splay9
  @Splay8:
              PUSH    AX
              MOV     AX,SI
              LES     SI,[RightP]
              ADD     SI,DI
              MOV     DX,[ES:SI]
{              mov     dx, [Offset Right + di]}    { ; perform a right zig-zag
}              LES     SI,[LeftP]
              MOV     [ES:SI+BX],DX
{              mov     [Offset Left + bx], dx}
              xchg    bx, dx
              LES     SI,[MomP]
              MOV     [ES:SI+BX],DX
{              mov     [Offset Mom + bx], dx}
              LES     SI,[LeftP]
              ADD     SI,DI
              MOV     BX,[ES:SI]
{              mov     bx, [Offset Left + di]}
              LES     SI,[RightP]
              ADD     SI,AX
              MOV     [ES:SI],BX
{              mov     [Offset Right + si], bx}
              LES     SI,[MomP]
              MOV     [ES:SI+BX],AX
{              mov     [Offset Mom + bx], si}
              mov     bx, dx
              LES     SI,[RightP]
              ADD     SI,DI
              MOV     [ES:SI],BX
{              mov     [Offset Right + di], bx}
              LES     SI,[LeftP]
              ADD     SI,DI
              MOV     [ES:SI],AX
{              mov     [Offset Left + di], si}
              LES     SI,[MomP]
              ADD     SI,AX
              MOV     [ES:SI],DI
              LES     SI,[MomP]
              MOV     [ES:SI+BX],DI
{              mov     [Offset Mom + si], di
              mov     [Offset Mom + bx], di}
              MOV     SI,AX
              POP     AX
  @Splay9:    mov     si, cx
              cmp     si, NUL
              ja      @Splay10
              PUSH    DI
              LES     DI,[LeftP]
              ADD     DI,SI
              CMP     BX,[ES:DI]
              POP     DI
{              cmp     bx, [Offset Left + si]}
              jne     @Splay10
              PUSH    BX
              LES     BX,[LeftP]
              MOV     [ES:BX+SI],DI
              POP     BX
{              mov     [Offset Left + si], di}
              jmp     @Splay11
  @Splay10:
              PUSH    BX
              LES     BX,[RightP]
              MOV     [ES:BX+SI],DI
              POP     BX
{              mov     [Offset Right + si], di}
  @Splay11:
              PUSH    BX
              LES     BX,[MomP]
              MOV     [ES:BX+DI],SI
              POP     BX
{              mov     [Offset Mom + di], si}
              jmp     @Splay1
  @end:
End; { SPlay }



Procedure InsertNode; assembler;
{
  insertNode : insert the new node to the corresponding tree.  Note that the
               position of a string in the buffer also served as the node
               number.
             ENTRY : di = position in the buffer
}
Asm
              push    si
              push    dx
              push    cx
              push    bx
              mov     dx, 1
              xor     ax, ax
              mov     matchLen, ax
              mov     height, ax
              LES     SI,[TextBufP]
              ADD     SI,DI
              MOV     AL,BYTE PTR [ES:SI]
{             mov     al, byte ptr [Offset TextBuf + di]}
              shl     di, 1
              add     ax, N + 1
              shl     ax, 1
              mov     si, ax
              mov     ax, NUL
              PUSH    BX
              LES     BX,[RightP]
              MOV     WORD PTR [ES:BX+DI],AX
{              mov     word ptr [Offset Right + di], ax}
              LES     BX,[LeftP]
              MOV     WORD PTR [ES:BX+DI],AX
              POP     BX
{              mov     word ptr [Offset Left + di], ax}
  @Ins1:inc     height
              cmp     dx, 0
              jl      @Ins3
              PUSH    DI
              LES     DI,[RightP]
              ADD     DI,SI
              MOV     AX,WORD PTR [ES:DI]
              POP     DI
{              mov     ax, word ptr [Offset Right + si]}
              cmp     ax, NUL
              je      @Ins2
              mov     si, ax
              jmp     @Ins5
  @Ins2:
              PUSH    BX
              LES     BX,[RightP]
              MOV     WORD PTR [ES:BX+SI],DI
{              mov     word ptr [Offset Right + si], di}
              LES     BX,[MomP]
              MOV     WORD PTR [ES:BX+DI],SI
              POP     BX
{              mov     word ptr [Offset Mom + di], si}
              jmp     @Ins11
  @Ins3:
              PUSH    BX
              LES     BX,[LeftP]
              ADD     BX,SI
              MOV     AX,WORD PTR [ES:BX]
              POP     BX
{              mov     ax, word ptr [Offset Left + si]}
              cmp     ax, NUL
              je      @Ins4
              mov     si, ax
              jmp     @Ins5
  @Ins4:
              PUSH    BX
              LES     BX,[LeftP]
              ADD     BX,SI
              MOV     WORD PTR [ES:BX],DI
{              mov     word ptr [Offset Left + si], di}
              LES     BX,[MomP]
              ADD     BX,DI
              MOV     WORD PTR [ES:BX],SI
              POP     BX
{              mov     word ptr [Offset Mom + di], si}
              jmp     @Ins11
  @Ins5:      mov     bx, 1
              shr     si, 1
              shr     di, 1
              xor     ch, ch
              xor     dh, dh
  @Ins6:
              PUSH    SI
              LES     SI,[TextBufP]
              ADD     SI,DI
              MOV     DL,BYTE PTR [ES:SI+BX]
              POP     SI
              PUSH    DI
              LES     DI,[TextBufP]
              ADD     DI,SI
              MOV     CL,BYTE PTR [ES:DI+BX]
              POP     DI
{              mov     dl, byte ptr [Offset Textbuf + di + bx]
              mov     cl, byte ptr [Offset TextBuf + si + bx]}
              sub     dx, cx
              jnz     @Ins7
              inc     bx
              cmp     bx, F
              jb      @Ins6
  @Ins7:      shl     si, 1
              shl     di, 1
              cmp     bx, matchLen
              jbe     @Ins1
              mov     ax, si
              shr     ax, 1
              mov     matchPos, ax
              mov     matchLen, bx
              cmp     bx, F
              jb      @Ins1
  @Ins8:
              PUSH    CX
              LES     BX,[MomP]
              MOV     AX,WORD PTR [ES:BX+SI]
{              mov     ax, word ptr [Offset Mom + si]}
              LES     BX,[MomP]
              MOV     WORD PTR [ES:BX+DI],AX
{              mov     word ptr [Offset Mom + di], ax}
              LES     BX,[LeftP]
              MOV     CX,WORD PTR [ES:BX+SI]
{              mov     bx, word ptr [Offset Left + si]}
              LES     BX,[LeftP]
              MOV     WORD PTR [ES:BX+DI],CX
{              mov     word ptr [Offset Left + di], bx}
              LES     BX,[MomP]
              ADD     BX,CX
              MOV     WORD PTR [ES:BX],DI
{              mov     word ptr [Offset Mom + bx], di}
              LES     BX,[RightP]
              MOV     CX,WORD PTR [ES:BX+SI]
{              mov     bx, word ptr [Offset Right + si]}
              LES     BX,[RightP]
              MOV     WORD PTR [ES:BX+DI],CX
{              mov     word ptr [Offset Right + di], bx}
              LES     BX,[MomP]
              ADD     BX,CX
              MOV     WORD PTR [ES:BX],DI
{              mov     word ptr [Offset Mom + bx], di}
              LES     BX,[MomP]
              MOV     CX,WORD PTR [ES:BX+SI]
{              mov     bx, word ptr [Offset Mom + si]}
              MOV     BX,CX
              POP     CX
              PUSH    DI
              LES     DI,[RightP]
              CMP     SI,WORD PTR [ES:DI+BX]
              POP     DI
{              cmp     si, word ptr [Offset Right + bx]}
              jne     @Ins9
              PUSH    SI
              LES     SI,[RightP]
              MOV     WORD PTR [ES:SI+BX],DI
              POP     SI
{              mov     word ptr [Offset Right + bx], di}
              jmp     @Ins10
  @Ins9:
              PUSH    SI
              LES     SI,[LeftP]
              MOV     WORD PTR [ES:SI+BX],DI
              POP     SI
{              mov     word ptr [Offset Left + bx], di}
  @Ins10:
              PUSH    DI
              LES     DI,[MomP]
              ADD     DI,SI
              MOV     WORD PTR [ES:DI],NUL
              POP     DI
{              mov     word ptr [Offset Mom + si], NUL}
  @Ins11:     cmp     height, 30
              jb      @Ins12
              call    Splay
  @Ins12:     pop     bx
              pop     cx
              pop     dx
              pop     si
              shr     di, 1
End; { InsertNode }


Procedure DeleteNode; assembler;
{
   deleteNode : delete the node from the tree

            ENTRY : SI = position in the buffer
}
Asm
              push    di
              push    bx
              shl     si, 1
              PUSH    DI
              LES     DI,[MomP]
              ADD     DI,SI
              CMP     WORD PTR [ES:DI],NUL
              POP     DI
{              cmp     word ptr [Offset Mom + si], NUL}   { ; if it has no
parent then exit }              je      @del7
              PUSH    DI
              LES     DI,[RightP]
              ADD     DI,SI
              CMP     WORD PTR [ES:DI],NUL
              POP     DI
{              cmp     word ptr [Offset Right + si], NUL} { ; does it have
right child ? }              je      @del8
              PUSH    BX
              LES     BX,[LeftP]
              MOV     DI,WORD PTR [ES:BX+SI]
              POP     BX
{              mov     di, word ptr [Offset Left + si] }  { ; does it have left
child ? }              cmp     di, NUL
              je      @del9
              PUSH    SI
              LES     SI,[RightP]
              ADD     SI,DI
              MOV     AX,WORD PTR [ES:SI]
              POP     SI
{              mov     ax, word ptr [Offset Right + di]}  { ; does it have
right grandchild ? }              cmp     ax, NUL
              je      @del2                             { ; if no then skip }
  @del1:      mov     di, ax                            { ; find the rightmost
node in }              PUSH    SI
              LES     SI,[RightP]
              ADD     SI,DI
              MOV     AX,WORD PTR [ES:SI]
              POP     SI
{              mov     ax, word ptr [Offset Right + di] } { ;   the right
subtree }              cmp     ax, NUL
              jne     @del1
              PUSH    CX
              MOV     CX,SI
              LES     SI,[MomP]
              ADD     SI,DI
              MOV     BX,WORD PTR [ES:SI]
{              mov     bx, word ptr [Offset Mom + di] }   { ; move this node as
the root of }              LES     SI,[LeftP]
              ADD     SI,DI
              MOV     AX,WORD PTR [ES:SI]
{              mov     ax, word ptr [Offset Left + di]}   { ;   the subtree }
              LES     SI,[RightP]
              MOV     WORD PTR [ES:SI+BX],AX
{              mov     word ptr [Offset Right + bx], ax}
              xchg    ax, bx
              LES     SI,[MomP]
              MOV     WORD PTR [ES:SI+BX],AX
{              mov     word ptr [Offset Mom + bx], ax}
              LES     SI,[LeftP]
              ADD     SI,CX
              MOV     BX,WORD PTR [ES:SI]
{              mov     bx, word ptr [Offset Left + si]}
              LES     SI,[LeftP]
              ADD     SI,DI
              MOV     WORD PTR [ES:SI],BX
{              mov     word ptr [Offset Left + di], bx}
              LES     SI,[MomP]
              MOV     WORD PTR [ES:SI+BX],DI
{              mov     word ptr [Offset Mom + bx], di}
              MOV     SI,CX
              POP     CX
  @del2:
              PUSH    CX
              MOV     CX,SI
              LES     SI,[RightP]
              ADD     SI,CX
              MOV     BX,WORD PTR [ES:SI]
{              mov     bx, word ptr [Offset Right + si]}
              LES     SI,[RightP]
              ADD     SI,DI
              MOV     WORD PTR [ES:SI],BX
{              mov     word ptr [Offset Right + di], bx}
              LES     SI,[MomP]
              MOV     WORD PTR [ES:SI+BX],DI
              MOV     SI,CX
              POP     CX
{              mov     word ptr [Offset Mom + bx], di}
  @del3:
              PUSH    CX
              MOV     CX,DI
              LES     DI,[MomP]
              ADD     DI,SI
              MOV     BX,WORD PTR [ES:DI]
{              mov     bx, word ptr [Offset Mom + si]}
              LES     DI,[MomP]
              ADD     DI,CX
              MOV     WORD PTR [ES:DI],BX
{              mov     word ptr [Offset Mom + di], bx}
              MOV     DI,CX
              POP     CX
              PUSH    DI
              LES     DI,[RightP]
              CMP     SI,WORD PTR [ES:DI+BX]
              POP     DI
{              cmp     si, word ptr [Offset Right + bx]}
              jne     @del4
              PUSH    SI
              LES     SI,[RightP]
              MOV     WORD PTR [ES:SI+BX],DI
              POP     SI
{              mov     word ptr [Offset Right + bx], di}
              jmp     @del5
  @del4:
              PUSH    SI
              LES     SI,[LeftP]
              MOV     WORD PTR [ES:SI+BX],DI
              POP     SI
{              mov     word ptr [Offset Left + bx], di}
  @del5:
              PUSH    DI
              LES     DI,[MomP]
              ADD     DI,SI
              MOV     WORD PTR [ES:DI],NUL
              POP     DI
{              mov     word ptr [Offset Mom + si], NUL}
  @del7:      pop     bx
              pop     di
              shr     si, 1
              jmp     @end;
  @del8:
              PUSH    BX
              LES     BX,[LeftP]
              MOV     DI,WORD PTR [ES:BX+SI]
              POP     BX
{              mov     di, word ptr [Offset Left + si]}
              jmp     @del3
  @del9:
              PUSH    BX
              LES     BX,[RightP]
              MOV     DI,WORD PTR [ES:BX+SI]
              POP     BX
{              mov     di, word ptr [Offset Right + si]}
              jmp     @del3
  @end:
End; { DeleteNode }


Procedure Encode; assembler;
Asm
              call    initTree
              xor     bx, bx
              mov     [Offset CodeBuf + bx], bl
              mov     dx, 1
              mov     ch, dl
              xor     si, si
              mov     di, N - F
  @Encode2:   call    getc
              jc      @Encode3
              PUSH    SI
              LES     SI,[TextBufP]
              ADD     SI,DI
              MOV     BYTE PTR [ES:SI+BX],AL
              POP     SI
{              mov     byte ptr [Offset TextBuf +di + bx], al}
              inc     bx
              cmp     bx, F
              jb      @Encode2
  @Encode3:   or      bx, bx
              jne     @Encode4
              jmp     @Encode19
  @Encode4:   mov     cl, bl
              mov     bx, 1
              push    di
              sub     di, 1
  @Encode5:   call    InsertNode
              inc     bx
              dec     di
              cmp     bx, F
              jbe     @Encode5
              pop     di
              call    InsertNode
  @Encode6:   mov     ax, matchLen
              cmp     al, cl
              jbe     @Encode7
              mov     al, cl
              mov     matchLen, ax
  @Encode7:   cmp     al, THRESHOLD
              ja      @Encode8
              mov     matchLen, 1
              or      byte ptr codeBuf, ch
              mov     bx, dx
              PUSH    SI
              LES     SI,[TextBufP]
              ADD     SI,DI
              MOV     AL,BYTE PTR [ES:SI]
              POP     SI
{              mov     al, byte ptr [Offset TextBuf + di]}
              mov     byte ptr [Offset CodeBuf + bx], al
              inc     dx
              jmp     @Encode9
  @Encode8:   mov     bx, dx
              mov     al, byte ptr matchPos
              mov     byte ptr [Offset Codebuf + bx], al
              inc     bx
              mov     al, byte ptr (matchPos + 1)
              push    cx
              mov     cl, 4
              shl     al, cl
              pop     cx
              mov     ah, byte ptr matchLen
              sub     ah, THRESHOLD + 1
              add     al, ah
              mov     byte ptr [Offset Codebuf + bx], al
              inc     bx
              mov     dx, bx
  @Encode9:   shl     ch, 1
              jnz     @Encode11
              xor     bx, bx
  @Encode10:  mov     al, byte ptr [Offset CodeBuf + bx]
              call    putc
              inc     bx
              cmp     bx, dx
              jb      @Encode10
              mov     dx, 1
              mov     ch, dl
              mov     byte ptr codeBuf, dh
  @Encode11:  mov     bx, matchLen
              mov     lastLen, bx
              xor     bx, bx
  @Encode12:  call    getc
{              jc      @Encode14}
              jc      @Encode15
              push    ax
              call    deleteNode
              pop     ax
              PUSH    DI
              LES     DI,[TextBufP]
              ADD     DI,SI
              stosb
              POP     DI
{              mov     byte ptr [Offset TextBuf + si], al}
              cmp     si, F - 1
              jae     @Encode13
              PUSH    DI
              LES     DI,[TextBufP]
              ADD     DI,SI
              MOV     BYTE PTR [ES:DI+N],AL
              POP     DI
{              mov     byte ptr [Offset TextBuf + si + N], al}
  @Encode13:  inc     si
              and     si, N - 1
              inc     di
              and     di, N - 1
              call    insertNode
              inc     bx
              cmp     bx, lastLen
              jb      @Encode12
(*  @Encode14:  sub     printCount, bx
              jnc     @Encode15
              mov     ax, printPeriod
              mov     printCount, ax
              push    dx                 { Print out a period as a sign. }
              mov     dl, DBLARROW
              mov     ah, 2
              int     21h
              pop     dx *)
  @Encode15:  cmp     bx, lastLen
              jae     @Encode16
              inc     bx
              call    deleteNode
              inc     si
              and     si, N - 1
              inc     di
              and     di, N - 1
              dec     cl
              jz      @Encode15
              call    insertNode
              jmp     @Encode15
  @Encode16:  cmp     cl, 0
              jbe     @Encode17
              jmp     @Encode6
  @Encode17:  cmp     dx, 1
              jb      @Encode19
              xor     bx, bx
  @Encode18:  mov     al, byte ptr [Offset Codebuf + bx]
              call    putc
              inc     bx
              cmp     bx, dx
              jb      @Encode18
  @Encode19:
End; { Encode }

Procedure Decode; assembler;
Asm
              xor     dx, dx
              mov     di, N - F
  @Decode2:   shr     dx, 1
              or      dh, dh
              jnz     @Decode3
              call    getc
              jc      @Decode9
              mov     dh, 0ffh
              mov     dl, al
  @Decode3:   test    dx, 1
              jz      @Decode4
              call    getc
              jc      @Decode9
              PUSH    SI
              LES     SI,[TextBufP]
              ADD     SI,DI
              MOV     BYTE PTR [ES:SI],AL
              POP     SI
{              mov     byte ptr [Offset TextBuf + di], al}
              inc     di
              and     di, N - 1
              call    putc
              jmp     @Decode2
  @Decode4:   call    getc
              jc      @Decode9
              mov     ch, al
              call    getc
              jc      @Decode9
              mov     bh, al
              mov     cl, 4
              shr     bh, cl
              mov     bl, ch
              mov     cl, al
              and     cl, 0fh
              add     cl, THRESHOLD
              inc     cl
  @Decode5:   and     bx, N - 1
              PUSH    SI
              LES     SI,[TextBufP]
              MOV     AL,BYTE PTR [ES:SI+BX]
              ADD     SI,DI
              MOV     BYTE PTR [ES:SI],AL
              POP     SI
{              mov     al, byte ptr [Offset TextBuf + bx]
              mov     byte ptr [Offset TextBuf + di], al}
              inc     di
              and     di, N - 1
              call    putc
              inc     bx
              dec     cl
              jnz     @Decode5
              jmp     @Decode2
  @Decode9:
End; { Decode }

Function LZInit : boolean;
Begin
  if Initialized then Exit;
  LZInit := False;
  New(InBufP);
  New(OutBufP);
  New(TextBufP);
  New(LeftP);
  New(MomP);
  New(RightP);
  Initialized := (InBufP <> nil) and (OutBufP <> nil) and
    (TextBufP <> nil) and (LeftP <> nil) and (MomP <> nil) and (RightP <> nil);
  if Initialized then LZInit := True else
  begin
    Initialized := True;
    LZDone
  end
End; { LZInit }

Procedure LZDone;
Begin
  if Initialized then
  begin
    Dispose(InBufP);
    Dispose(OutBufP);
    Dispose(RightP);
    Dispose(MomP);
    Dispose(LeftP);
    Dispose(TextBufP);
    Initialized := False
  end
End; { LZDone }

Procedure LZSquash;
Begin
  if Initialized then
  begin
    InBufPtr := LZRWBufSize;
    InBufSize := LZRWBufSize;
    OutBufPtr := 0;
    Height := 0;
    MatchPos := 0;
    MatchLen := 0;
    LastLen := 0;

    FillChar(TextBufP^, SizeOf(TLZTextBuf), 0);
    FillChar(LeftP^, SizeOf(TLeftMomTree), 0);
    FillChar(MomP^, SizeOf(TLeftMomTree), 0);
    FillChar(RightP^, SizeOf(TRightTree), 0);
    FillChar(CodeBuf, SizeOf(CodeBuf), 0);

    LZReadProc := ReadProc;
    LZWriteProc := WriteProc;

    Encode;
    LZSS_Write
  end
End; { LZSquash }

Procedure LZUnSquash;
Begin
  if Initialized then
  begin
    InBufPtr := LZRWBufSize;
    InBufSize := LZRWBufSize;
    OutBufPtr := 0;
    FillChar(TextBufP^, SizeOf(TLZTextBuf), 0);

    LZReadProc := ReadProc;
    LZWriteProc := WriteProc;

    Decode;
    LZSS_Write
  end
End; { LZUnSquash }

{$IFDEF Windows}
Function HeapFunc(Size : word) : integer; far; assembler;
Asm
  MOV AX,1
End; { HeapFunc }
{$ENDIF}

Begin
{$IFDEF Windows}
  HeapError := @HeapFunc;
{$ENDIF}
  Initialized := False
End. { LZSSUNIT }








Program LZSSDemo;
{ Copyright (c) 1994 by Andrew Eigus   Fidonet: 2:5100/33 }
{ Demonstrates the use of LZSSUnit (LZSSUNIT.PAS), Public Domain }

uses LZSSUnit;

var InFile, OutFile : file;

Function ToUpper(S : string) : string; assembler;
Asm
  PUSH DS
  CLD
  LDS SI,S
  LES DI,@Result
  LODSB
  STOSB
  XOR AH,AH
  XCHG AX,CX
  JCXZ @@3
@@1:
  LODSB
  CMP AL,'a'
  JB  @@2
  CMP AL,'z'
  JA  @@2
  SUB AL,20h
@@2:
  STOSB
  LOOP @@1
@@3:
  POP DS
End; { ToUpper }

Function ReadProc(var ReadBuf; var NumRead : word) : word; far;
Begin
  BlockRead(InFile, ReadBuf, LZRWBufSize, NumRead);
  Write(#13, FilePos(InFile), ' -> ')
End; { ReadProc }

Function WriteProc(var WriteBuf; Count : word; var NumWritten : word) : word;
far;Begin
  BlockWrite(OutFile, WriteBuf, Count, NumWritten);
  Write(FilePos(OutFile), #13)
End; { WriteProc }

Begin
  if ParamCount < 2 then
  begin
    WriteLn('Usage: LZSSDEMO   [unsquash]');
    Halt(1)
  end;
  if not LZInit then
  begin
    WriteLn('Not enough memory');
    Halt(8)
  end;
  Assign(InFile, ParamStr(1));
  Reset(InFile, 1);
  if IOResult = 0 then
  begin
    Assign(OutFile, ParamStr(2));
    Rewrite(OutFile, 1);
    if IOResult = 0 then
    begin
      if ToUpper(ParamStr(3)) =  'UNSQUASH' then
        LZUnSquash(ReadProc, WriteProc)
      else
        LZSquash(ReadProc, WriteProc);
      Close(OutFile)
    end else WriteLn('Cannot create output file');
    Close(InFile)
  end else WriteLn('Cannot open input file');
  LZDone;
  WriteLn
End.


{
So boys and girls...

Here is just a ritual doc file, couz i hate to write READMEs! ;)
Okay, LZSSUNIT.PAS is my source unit for Borland (Turbo) Pascal for
implementation of LZSS (LZ77) compression algorithm in your programs.
It is designed to work with all three DOS, DPMI and Windows platforms
of Borland Pascal 7.0. Extremely clear example to show the use of this
unit and very easy implementation.

One note: I figured out that it compresses and decompresses files faster
and better! than the similar Microsoft COMPRESS/EXPAND utilities used by slow
Microsoft Setup program. Microsoft COMPRESS uses the same algo, however.

The source code provided is all Public Domain and thus no charge is
required for the author. Use at your own risk and modify as you wish.
I have am also not against of putting it in the new SWAG, eh Gayle?

Thanks send to:

Mr. Byte of TR/-\NC3T3CHN0PH0BiA
SysOp of AndRew's BBS  * +371-2-559777
Fidonet: 2:5100/33
Internet: aeigus@fgate.castle.riga.lv, aeigus@kristin.cclu.lv

If you still love the source so much, you are still welcome to send
some "support" fee to:

Andrew Eigus
Heinrichstr. 76
CH-8005 Zurich
}