home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / ZOO21E.EXE / LZC.ASM < prev    next >
Assembly Source File  |  1991-07-14  |  14KB  |  620 lines

  1. title    Lempel-Ziv Compressor
  2. ; $Source: /usr/home/dhesi/zoo/RCS/lzc.asm,v $
  3. ; $Id: lzc.asm,v 1.4 91/07/07 09:36:18 dhesi Exp $
  4.  
  5. ;Derived from Tom Pfau's public domain assembly code.
  6. ;The contents of this file are hereby released to the public domain.
  7. ;                                   -- Rahul Dhesi 1988/08/24
  8.  
  9. ; UNBUF_IO    equ    1        ;use unbuffered I/O
  10.  
  11. public    _lzc
  12.  
  13.     include asmconst.ai
  14.     include macros.ai
  15.  
  16. check_gap    equ    4000        ;Check ratio every so many bits
  17. scale_limit    equ    32000        ;scale down if bitsout > scale_limit
  18. rat_thresh    equ    0ffffh        ;don't reset if rat > rat_thresh
  19.  
  20. ;Hash table entry
  21. hash_rec    struc
  22. first    dw    ?            ; First entry with this value
  23. next    dw    ?            ; Next entry along chain
  24. char    db    ?            ; Suffix char
  25. hash_rec    ends
  26.  
  27. extrn    docrc:near            ;Procedure for block CRC in lzd.asm
  28.  
  29. ifdef    UNBUF_IO
  30. extrn    _read:near
  31. extrn    _blockwrite:near
  32. else
  33. extrn    _zooread:near
  34. extrn    _zoowrite:near
  35. endif
  36. extrn   _outchar:near
  37.  
  38. ;Declare Segments
  39. _TEXT    segment byte public 'CODE'
  40. _TEXT    ends
  41.  
  42. DGROUP  group   _DATA
  43.         assume ds:DGROUP,es:DGROUP
  44.  
  45. _DATA   segment word public 'DATA'
  46. extrn    _out_buf_adr:word        ;address of C output buffer
  47. extrn    _in_buf_adr:word        ;address of C input buffer
  48. extrn    memflag:byte            ;got memory? flag
  49.  
  50. save_sp        dw    ?
  51.  
  52. bytesin        dw    ?        ;count of bytes read
  53. bitsout        dw    ?        ;count of bits written
  54. ratio        dw    ?        ;recent ratio
  55. ratflag        db    ?        ;flag to remind us to check ratio
  56.         even
  57. bit_interval    dw    ?        ;interval at which to test ratio
  58.  
  59. input_handle    dw    ?
  60. input_seg       dw      ?
  61. output_handle    dw    ?
  62. output_seg      dw      ?
  63. hash_seg    dw    ?
  64. prefix_code    dw    ?
  65. free_code    dw    ?
  66. max_code    dw    ?
  67. nbits        dw    ?
  68. k        db    ?
  69.         even
  70. bit_offset    dw    ?
  71. input_offset    dw    0
  72. input_size    dw    0
  73. ifdef OS2
  74. selector    dw    0
  75. endif
  76. _DATA   ends
  77.  
  78. memory    segment para public 'memory'
  79. hash    label    hash_rec
  80. memory    ends
  81.  
  82. add_code macro
  83.     local    ac1,ac2,ac3
  84.     mov    bx,free_code        ;Get code to use
  85.     push    ds            ;point to hash table
  86.     mov    ds,hash_seg
  87.     or    di,di            ;First use of this prefix?
  88.     jz    ac1            ;zero means yes
  89.     mov    [si].next,bx        ;point last use to new entry
  90.     jmp    short ac2
  91. ac1:    mov    [si].first,bx        ;Point first use to new entry
  92. ac2:    cmp    bx,maxmax        ;Have we reached code limit?
  93.     je    ac3            ;equal means yes, just return
  94.  
  95.     ;call    index            ;get address of new entry
  96.     call_index            ;macro for speed
  97.  
  98.     mov    [si].first,-1        ;initialize pointers
  99.     mov    [si].next,-1
  100.     mov    [si].char,al        ;save suffix char
  101.     inc    es:free_code        ;adjust next code
  102. ac3:    pop    ds            ;restore seg reg
  103.     endm
  104.  
  105. read_char macro                ;Macro for speed
  106.     local m$1,m$2,m$3,m$4
  107.     inc    [bytesin]        ;Maintain input byte count for ratio
  108.     mov    di,input_offset        ;Anything left in buffer?
  109.     cmp    di,input_size
  110.     jb    m$1            ;less means yes
  111.  
  112.     mov    cx,inbufsiz
  113.     call    doread            ;read block
  114.  
  115.     cmp    ax,-1
  116.     jnz    m$3
  117.     jmp    IO_err            ;input error
  118. m$3:
  119.     mov    dx,_in_buf_adr        ;for docrc
  120.     call    docrc
  121.     or    ax,ax            ;Anything left?
  122.     jz    m$2            ;zero means no, finished
  123.     mov    input_size,ax        ;Save bytes read
  124.     mov    input_offset,0        ;Point to beginning of buffer
  125.     mov    di,0
  126. m$1:
  127.     mov    si,_in_buf_adr
  128.     add    si,di
  129.     lodsb                ;Read it in
  130.     inc    input_offset        ;Adjust pointer
  131.     clc                ;Success
  132.     jmp    short m$4
  133. m$2:    stc                ;Nothing left
  134. m$4:
  135.     endm
  136.  
  137. ;Start writing code
  138. _TEXT   segment
  139.         assume  cs:_TEXT,ds:DGROUP,es:DGROUP,ss:nothing
  140.  
  141. _lzc    proc    near
  142.     push    bp            ;Standard C entry code
  143.     mov    bp,sp
  144.     push    di
  145.     push    si
  146.  
  147.     push    ds            ;Save ds to be sure
  148.     mov    bx,ds
  149.     mov    es,bx
  150.  
  151. ;Get two parameters, both integers, that are input file handle and
  152. ;output file handle
  153. ifdef UNBUF_IO
  154.     mov    ax,[bp+4]
  155.     mov    [input_handle],ax
  156.     mov    ax,[bp+6]
  157.     mov    [output_handle],ax
  158. else
  159.     mov    ax,[bp+4]
  160.     mov    [input_handle],ax
  161.     mov    ax,[bp+6]
  162.     mov    [input_seg],ax
  163.     mov    ax,[bp+8]
  164.     mov    [output_handle],ax
  165.     mov    ax,[bp+10]
  166.     mov    [output_seg],ax
  167. endif
  168.  
  169.     call    compress        ;Compress file
  170.                     ;Status received back in AX
  171.     pop    ds
  172.     pop    si            ;Standard C return code
  173.     pop    di
  174.     mov    sp,bp
  175.     pop    bp
  176.     ret
  177.  
  178. _lzc    endp
  179.  
  180. compress    proc    near
  181.     mov    [save_sp],sp        ;Save SP in case of error return
  182.  
  183. ;Initialize variables for serial re-entrancy
  184.     mov    [bit_offset],0
  185.     mov    [input_offset],0
  186.     mov    [input_size],0
  187.  
  188.     test    memflag,0ffH        ;Memory allocated?
  189.     jnz    gotmem            ;If allocated, continue
  190. ifdef OS2
  191.         invoke  DOSALLOCSEG, maxmax*5+512, addr selector, 0
  192.         or      ax,ax
  193.         je      ok1
  194.     jmp    MALLOC_err1
  195. ok1:
  196.         mov     ax,selector
  197.         jmp     short here1
  198. else
  199.     malloc    <((maxmax * 5) / 16 + 20)>    ;allocate it
  200.     jnc    here1
  201.     jmp    MALLOC_err1
  202. endif
  203. here1:
  204.     mov    hash_seg,ax        ;Save segment address of mem block
  205.     mov    memflag,0ffh        ;Set flag to remind us later
  206. gotmem:
  207.  
  208. l1:    call    init_table        ;Initialize the table and some vars
  209.     mov    ax,clear        ;Write a clear code
  210.     call    write_code
  211.     ;call    read_char        ;Read first char
  212.     read_char            ;macro for speed
  213. l4:
  214.  
  215. ;new code to check compression ratio
  216.     test    [ratflag],0FFH        ;Time to check ratio?
  217.     jz    rd$1            ;Skip checking if zero
  218.     call    check_ratio
  219. rd$1:
  220.     xor    ah,ah            ;Turn char into code
  221. l4a:    mov    prefix_code,ax        ;Set prefix code
  222.     ;call    read_char        ;Read next char
  223.     read_char            ;macro for speed
  224.     jc    l17            ;Carry means eof
  225.     mov    k,al            ;Save char in k
  226.     mov    bx,prefix_code        ;Get prefix code
  227.  
  228.     call    lookup_code        ;See if this pair in table
  229.  
  230.     jnc    l4a            ;nc means yes, new code in ax
  231.     ;call    add_code        ;Add pair to table
  232.     add_code            ;Macro for speed
  233.     push    bx            ;Save new code
  234.     mov    ax,prefix_code        ;Write old prefix code
  235.     call    write_code
  236.     pop    bx
  237.     mov    al,k            ;Get last char
  238.     cmp    bx,max_code        ;Exceed code size?
  239.  
  240.     jnb    l4$
  241.     jmp    l4
  242. l4$:
  243.     cmp    nbits,maxbits        ;Currently less than maxbits?
  244.     jb    l14            ;yes
  245.     mov    ax,clear        ;Write a clear code
  246.     call    write_code
  247.     call    init_table        ;Reinit table
  248.     mov    al,k            ;get last char
  249.     jmp    l4            ;Start over
  250. l14:    inc    nbits            ;Increase number of bits
  251.     shl    max_code,1        ;Double max code size
  252.     jmp    l4            ;Get next char
  253. l17:    mov    ax,prefix_code        ;Write last code
  254.     call    write_code
  255.     mov    ax,eof            ;Write eof code
  256.     call    write_code
  257.     mov    ax,bit_offset        ;Make sure buffer is flushed to file
  258.     cmp    ax,0
  259.     je    OK_ret
  260.     mov    dx,ax            ;dx <- ax
  261.     shr    ax,1
  262.     shr    ax,1
  263.     shr    ax,1            ;ax <- ax div 8
  264.     and    dx,07            ;dx <- ax mod 8
  265.                     ;If extra bits, make sure they get
  266.     je    l17a            ;written
  267.     inc    ax
  268. l17a:    call    flush
  269. OK_ret:
  270.     xor    ax,ax            ;Normal return -- compressed
  271.     ret
  272. IO_err:
  273.     mov    ax,2            ;I/O error return
  274.     mov    sp,[save_sp]        ;Restore stack pointer
  275.     ret
  276.  
  277. MALLOC_err1:                ;hash table alloc error
  278.     mov    ax,1            ;Malloc error return
  279.     mov    sp,[save_sp]        ;Restore stack pointer
  280.     ret
  281. compress    endp
  282.  
  283. init_table    proc    near
  284.     mov    [bytesin],0        ;Input byte count
  285.     mov    [bitsout],0        ;Output bit count
  286.     mov    [ratio],0
  287.     mov    [ratflag],0
  288.     mov    [bit_interval],check_gap
  289.  
  290.     mov    nbits,9            ;Set code size to 9
  291.     mov    max_code,512        ;Set max code to 512
  292.     push    es            ;Save seg reg
  293.     mov    es,hash_seg        ;Address hash table
  294.     mov    ax,-1            ;Unused flag
  295.     mov    cx,640            ;Clear first 256 entries
  296.     mov    di,offset hash        ;Point to first entry
  297. rep    stosw                ;Clear it out
  298.     pop    es            ;Restore seg reg
  299.     mov    free_code,first_free    ;Set next code to use
  300.     ret                ;done
  301. init_table    endp
  302.  
  303. write_code    proc    near
  304.     push    ax            ;Save code
  305.     mov    cx,nbits
  306.     add    [bitsout],cx        ;Maintain output bit count for ratio
  307.     sub    [bit_interval],cx
  308.     jg    rd$2            ;OK if not reached interval
  309.     mov    [ratflag],1        ;else set flag -- check ratio soon
  310. rd$2:
  311.     mov    ax,bit_offset        ;Get bit offset
  312.     mov    cx,nbits        ;Adjust bit offset by code size
  313.     add    bit_offset,cx
  314.  
  315.     mov    dx,ax            ;dx <- ax
  316.     shr    ax,1
  317.     shr    ax,1
  318.     shr    ax,1            ;ax <- ax div 8
  319.     and    dx,07            ;dx <- ax mod 8
  320.  
  321.     ;Now ax contains byte offset and
  322.     ;dx contains bit offset within that byte (I think)
  323.  
  324.     cmp    ax,outbufsiz-4        ;Approaching end of buffer?
  325.     jb    wc1            ;less means no
  326.     call    flush            ;Write the buffer
  327.  
  328.     push    dx            ;dx contains offset within byte
  329.     add    dx,nbits        ;adjust by code size
  330.     mov    bit_offset,dx        ;new bit offset
  331.     pop    dx            ;restore dx
  332.  
  333. ;ax is an index into output buffer.  Next, ax <- address of buffer[ax]
  334.     add    ax,[_out_buf_adr]
  335.     mov    si,ax            ;put in si
  336.     mov    al,byte ptr [si]    ;move byte to first position
  337.  
  338. ;put value of al into first byte of output buffer
  339.     push    bx
  340.     mov    bx,[_out_buf_adr]
  341.     mov    [bx],al
  342.     pop    bx
  343.     xor    ax,ax            ;Byte offset of zero
  344. wc1:    add    ax,[_out_buf_adr]    ;Point into buffer
  345.     mov    di,ax            ;Destination
  346.     pop    ax            ;Restore code
  347.     mov    cx,dx            ;offset within byte
  348.     xor    dx,dx            ;dx will catch bits rotated out
  349.     jcxz    wc3            ;If offset in byte is zero, skip shift
  350. wc2:    shl    ax,1            ;Rotate code
  351.     rcl    dx,1
  352.     loop    wc2
  353.     or    al,byte ptr [di]    ;Grab bits currently in buffer
  354. wc3:    stosw                ;Save data
  355.     mov    al,dl            ;Grab extra bits
  356.     stosb                ;and save
  357.     ret
  358. write_code    endp
  359.  
  360. flush    proc    near
  361.     push    ax            ;Save all registers
  362.     push    bx            ;AX contains number of bytes to write
  363.     push    cx
  364.     push    dx
  365.  
  366.     push    si            ;may not be necessary to save si & di
  367.     push    di
  368.  
  369.     push    ax            ;save byte count
  370.  
  371.     push    ax            ;byte count
  372.     push    _out_buf_adr        ;buffer address
  373. ifdef    UNBUF_IO
  374.     push    output_handle        ;zoofile
  375.     call    _blockwrite
  376. else
  377.     push    output_seg        ;zoofile
  378.     push    output_handle        ;zoofile
  379.     call    _zoowrite
  380. endif
  381.     add    sp,6
  382.  
  383.     pop    cx            ;recover byte count
  384.  
  385.     cmp    ax,cx
  386.  
  387.     jz    here2
  388.     jmp    IO_err        ;I/O error
  389.  
  390. here2:
  391.     pop    di
  392.     pop    si
  393.  
  394.     pop    dx
  395.     pop    cx
  396.     pop    bx
  397.     pop    ax
  398.     ret
  399. flush        endp
  400.  
  401. lookup_code    proc    near
  402.     push    ds            ;Save seg reg
  403.     mov    ds,hash_seg        ;point to hash table
  404.  
  405.     ;call    index            ;convert code to address
  406.     call_index            ;macro for speed
  407.  
  408.     mov    di,0            ;flag
  409.     mov    bx,[si].first
  410.     cmp    bx,-1            ;Has this code been used?
  411.     je    gc4            ;equal means no
  412.     inc    di            ;set flag
  413. gc2:
  414.     ;call    index            ;convert code to address
  415.     call_index            ;macro for speed
  416.  
  417.     cmp    [si].char,al        ;is char the same?
  418.     jne    gc3            ;ne means no
  419.     clc                ;success
  420.     mov    ax,bx            ;put found code in ax
  421.     pop    ds            ;restore seg reg
  422.     ret                ;done
  423. gc3:
  424.     mov    bx,[si].next
  425.     cmp    bx,-1            ;More left with this prefix?
  426.     je    gc4            ;equal means no
  427.     jmp    gc2            ;try again
  428. gc4:    stc                ;not found
  429.     pop    ds            ;restore seg reg
  430.     ret                ;done
  431. lookup_code    endp
  432.  
  433. comment #
  434. index    proc    near
  435.     mov    si,bx            ;si = bx * 5 (5 byte hash entries)
  436.     shl    si,1            ;si = bx * 2 * 2 + bx
  437.     shl    si,1
  438.     add    si,bx
  439.     ret
  440. index        endp
  441. # end comment
  442.  
  443. check_ratio    proc    near
  444.     push    ax
  445.  
  446. ;    mov    dl,'*'            ;'*' printed means checking ratio
  447. ;    call    sendout
  448.  
  449.     ;Getting ready to check ratios.  If bitsout is over scale_limit,
  450.     ;then we scale down both bitsout and bytesin by a factor
  451.     ;of 4.  This will avoid overflow.
  452.     mov    cx,[bitsout]
  453.     cmp    cx,scale_limit
  454.     jb    scale_ok
  455.  
  456.     mov    cl,2
  457.     shr    [bytesin],cl
  458.     shr    [bitsout],cl
  459.  
  460. scale_ok:
  461.     ;Note MASM bug:  "mov ah,high [bytesin]" and
  462.     ;"mov al,low [bytesin]" don't work.
  463.     mov    ah,byte ptr [bytesin]
  464.     mov    dl,byte ptr [bytesin+1]
  465.  
  466.     sub    al,al
  467.     sub    dh,dh            ;dx:ax = 8 * bitsin = 256 * bytesin
  468.     mov    cx,[bitsout]        ;cx <- bitsout
  469.     or    cx,cx            ;Division by zero?
  470.     jnz    candivide        ;No -- go ahead divide
  471.     mov    ax,0FFFFH        ;yes -- assume max poss value
  472.     jmp    short divided
  473. candivide:
  474.     ;Calculate cx as (bytesin * 256) / bitsout  = bitsin * 8 / bitsout
  475.     div    cx            ;ax <- rat <- dx:ax / cx
  476.     shl    ax,1
  477.     shl    ax,1            ;rat <- 4 * bytes_in / bytes_out
  478. divided:
  479.     ;Enter here with ax = rat = bitsin / bitsout.
  480.  
  481. ;    call print_data            ;print info for debugging
  482.  
  483.     ;If rat > rat_thresh then ratio is good;  do not reset table
  484. ;    cmp    ax,rat_thresh
  485. ;    ja    ratdone
  486.  
  487.     ;Compare rat against ratio
  488.     mov    bx,ax            ;save rat in bx
  489.     cmp    ax,[ratio]        ;cmp rat,ratio
  490.     jb    ratless            ;trouble if ratio is now smaller
  491.     mov    ax,[ratio]
  492.     call    mul7            ;ax <- 7 * ratio
  493.     add    ax,bx            ;ax = 7 * ratio + rat
  494.     shr    ax,1
  495.     shr    ax,1
  496.     shr    ax,1            ;ax = (7 * ratio + rat) / 8
  497.     mov    [ratio],ax        ;ratio = (7 * ratio + rat) / 8
  498.     jmp    short ratdone
  499. ratless:                ;ratio is going down, so...
  500.     mov    [bytesin],0
  501.     mov    [bitsout],0
  502.  
  503. ;    mov    dl,'#'            ;'#' printed means table reset
  504. ;    call    sendout
  505.  
  506.     mov    ax,clear        ;Write a clear code
  507.     call    write_code
  508.     call    init_table        ;Reinit table
  509. ratdone:
  510.     mov    [ratflag],0
  511.     mov    [bit_interval],check_gap
  512.     pop    ax
  513.     ret
  514. check_ratio    endp
  515.  
  516. comment #
  517. sendout    proc    near            ;char in dl send to console
  518.     push    ax
  519.         call    _outchar
  520.     pop    ax
  521.     ret
  522. sendout    endp
  523. # end comment
  524.  
  525. mul7    proc    near            ;multiply ax by 7
  526.     push    dx
  527.     mov    dx,7
  528.     mul    dx            ;dx:ax <- 7 * ax
  529.     pop    dx
  530.     ret
  531. mul7    endp
  532.  
  533. comment #
  534. mul3    proc    near            ;multiply ax by 3
  535.     push    dx
  536.     mov    dx,3
  537.     mul    dx            ;dx:ax <- 3 * ax
  538.     pop    dx
  539.     ret
  540. mul3    endp
  541. # end comment
  542.  
  543. comment #
  544. mul1_125 proc    near            ;multiply ax by 1.125
  545.     push    bx
  546.     mov    bx,ax
  547.     shr    bx,1
  548.     shr    bx,1
  549.     shr    bx,1            ;bx = n / 8
  550.     add    ax,bx            ;ax <- n + n / 8
  551.     pop    bx
  552.     ret
  553. mul1_125 endp
  554. # end comment
  555.  
  556. comment #
  557. print_data proc near
  558.     ;Debugging -- print bytesin, bitsout, rat, and ratio
  559.     push    ax
  560.     push    bx
  561.     push    cx
  562.     push    dx
  563.  
  564.     push    ax        ;print rat
  565.     call    _prtint
  566.     add    sp,2
  567.  
  568.     push    [ratio]        ;print ratio
  569.     call    _prtint
  570.     add    sp,2
  571.  
  572.     push    [bytesin]
  573.     call    _prtint
  574.     add    sp,2
  575.  
  576.     push    [bitsout]
  577.     call    _prtint
  578.     add    sp,2
  579.  
  580.     pop    dx
  581.     pop    cx
  582.     pop    bx
  583.     pop    ax
  584.     ret
  585. print_data endp
  586. # end comment
  587.  
  588. ;doread reads cx characters and stores them in input buffer
  589. ;return value from zooread is returned in ax
  590. doread    proc    near        ;reads block
  591.     push    bx
  592.     push    cx
  593.     push    dx
  594.     push    si
  595.     push    di
  596.  
  597.     push    cx            ;byte count
  598.     push    _in_buf_adr        ;buffer address
  599. ifdef    UNBUF_IO
  600.     push    input_handle        ;zoofile
  601.     call    _read
  602. else
  603.     push    input_seg        ;zoofile
  604.     push    input_handle        ;zoofile
  605.     call    _zooread
  606. endif
  607.     add    sp,6
  608.  
  609.     pop    di
  610.     pop    si
  611.     pop    dx
  612.     pop    cx
  613.     pop    bx
  614.     ret
  615. doread    endp
  616.  
  617. _TEXT    ends
  618.  
  619. end
  620.