home *** CD-ROM | disk | FTP | other *** search
- TITLE Compress - LZW compressor for GIF
- NAME Compress
- PAGE 60,132
- ; Comp.asm - Compress image data.
- ;
- ; Called by Microsoft C
- ;
- ; int Compress(code_size,get_byte_routine,put_byte_routine);
- ; unsigned int code_size;
- ; int (*get_byte_routine)();
- ; void (*put_byte_routine)(code);
- ; unsigned int code;
- ;
- ; Returns
- ;
- ; 0 Normal completion
- ; -1 Cannot allocate memory for hash table
- ; -2 Invalid code size
- ;
- ; Originated from lzcomp.asm by Tom Pfau, Digital Equipment Corporation
- ; C callable version by Steve Becquer (74155,767)
- ;
- ; Version 1.00 last revised 10/21/88
- ;
- ; GIF and 'Graphics Interchange Format' are trademarks (tm) of
- ; CompuServe Incorporated, an H&R Block Company.
-
- ; Equates
-
- code_size EQU [bp+4]
- get_byte_routine EQU [bp+6]
- put_byte_routine EQU [bp+8]
- maxmax EQU 4096 ; Max code + 1
-
- ; Hash table entry
-
- hash_rec STRUC
- first DW ? ; First entry with this value
- next DW ? ; Next entry along chain
- char DB ? ; Suffix char
- hash_rec ENDS
-
- ; Declare segments
-
- _TEXT SEGMENT BYTE PUBLIC 'CODE'
- _TEXT ENDS
- _DATA SEGMENT WORD PUBLIC 'DATA'
- _DATA ENDS
- memory SEGMENT PARA 'MEMORY'
- hash LABEL hash_rec
- memory ENDS
-
- DGROUP GROUP _DATA
- ASSUME CS:_TEXT,DS:DGROUP,SS:DGROUP,ES:DGROUP
-
- ; Entry coding
-
- _TEXT SEGMENT
-
- PUBLIC _Compress
-
- _Compress PROC NEAR
- push bp ; Save caller's registers
- mov bp,sp
- push di
- push si
- push es
-
- mov bx,1280 ; Allocate space for hash table
- mov ah,48h
- int 21h
- jnc memok
- mov ax,-1 ; Allocation error
- jmp l19
- memok: mov hash_seg,ax ; Save segment address
-
- mov ax,code_size ; Validate code size
- cmp ax,2 ; Code size < 2?
- jl cserr ; Yes, error
- cmp ax,8 ; Code size <= 8?
- jle init ; Yes, it's good
- cserr: mov ax,-2 ; Invalid code size
- jmp l19
-
- init: xor ax,ax ; Initialize variables
- mov bits_held,ax
- mov bits,ax
- mov bits+2,ax
- l1: call init_table ; Initialize table
- mov ax,clear ; Write a clear code
- call write_code
- push es
- call get_byte_routine ; Read first char
- pop es
- cmp ax,-1 ; End of data?
- je l18 ; Yes, nothing to do
- l4: xor ah,ah ; Turn char into code
- l4a: mov prefix_code,ax ; Set prefix code
- push es
- call get_byte_routine ; Read next char
- pop es
- cmp ax,-1 ; End of data?
- je l17 ; Yes
- mov k,al ; Save char in k
- mov bx,prefix_code ; Get prefix code
- call lookup_code ; See if this pair in table
- jnc l4a ; nc means yes, new code in ax
- call add_code ; Add pair to table
- push bx ; Save new code
- mov ax,prefix_code ; Write old prefix code
- call write_code
- pop bx
- mov al,k ; Get last char
- cmp bx,max_code ; Exceed code size?
- jl l4 ; Less means no
- cmp nbits,12 ; Currently less than 12 bits?
- jl l14 ; Yes
- mov ax,clear ; Write a clear code
- call write_code
- call init_table ; Reinit table
- mov al,k ; Get last char
- jmp short l4 ; Start over
- l14: inc nbits ; Increase number of bits
- shl max_code,1 ; Double max code size
- jmp short l4 ; Get next char
- l17: mov ax,prefix_code ; Write last code
- call write_code
- mov ax,eof ; Write eof code
- call write_code
- cmp bits_held,0 ; Make sure buffer is flushed to file
- je l18
- l17a: call flush
- l18: mov es,hash_seg ; Free hash table space
- mov ah,49h
- int 21h
- xor ax,ax ; Zero return
- l19: pop es ; Restore caller's registers
- pop si
- pop di
- mov sp,bp
- pop bp
- ret
-
- _compress ENDP
-
- _DATA SEGMENT
- clear DW ?
- eof DW ?
- hash_seg DW ?
- prefix_code DW ?
- free_code DW ?
- max_code DW ?
- nbits DW ?
- k DB ?
- _DATA ENDS
-
- init_table PROC NEAR
- mov cx,code_size ; Get code size
- mov ax,1
- shl ax,cl ; Compute clear code
- mov clear,ax
- inc ax ; Compute end of info code
- mov eof,ax
- inc ax ; Compute first free entry in
- mov free_code,ax ; Code table
- inc cx ; Compute bits per code
- mov nbits,cx
- mov ax,1
- shl ax,cl ; Compute maximum code value
- mov max_code,ax
- mov es,hash_seg ; Address hash table
- mov ax,-1 ; Unused flag
- mov cx,clear ; Clear root codes
- mov di,offset hash ; Point to first entry
- it1: stosw ; Clear it out
- add di,3 ; Bump to next entry
- loop it1
- push ds
- pop es ; Restore seg reg
- ret ; Done
- init_table ENDP
-
- write_code PROC NEAR
- push ax ; Save code
- wt1: cmp bits_held,8 ; Have enough for a byte?
- jl wt2 ; No
- mov ax,bits+2 ; Get bits held
- xor ah,ah ; Make a byte
- push es ; Preserve seg reg
- push ax ; Character to be passed
- call put_byte_routine ; Output byte
- add sp,2 ; Adjust stack
- pop es
- mov dx,bits ; Shift remaining bits right
- mov ax,bits+2
- mov al,ah
- mov ah,dl
- mov dl,dh
- xor dh,dh
- mov bits,dx
- mov bits+2,ax
- sub bits_held,8 ; Compute new bits held
- jmp short wt1 ; See if less than a byte left
- wt2: xor dx,dx ; Add new code bits
- pop ax
- mov cx,bits_held
- jcxz wt3 ; If zero, skip shift
- wt2a: shl ax,1 ; Shift new code
- rcl dx,1
- loop wt2a
- wt3: or bits,dx ; Save new bits held
- or bits+2,ax
- mov cx,nbits ; Compute new held count
- add bits_held,cx
- ret
- write_code ENDP
-
- _DATA SEGMENT
- bits_held DW ?
- bits DW 2 DUP (?)
- _DATA ENDS
-
- flush PROC NEAR
- fl1: cmp bits_held,0 ; Have any bits held?
- jle fl2 ; No
- mov ax,bits+2 ; Get bits held
- xor ah,ah ; Make a byte
- push es ; Preserve seg reg
- push ax ; Character to be passed
- call put_byte_routine ; Output byte
- add sp,2 ; Adjust stack
- pop es
- mov dx,bits ; Shift remaining bits right
- mov ax,bits+2
- mov al,ah
- mov ah,dl
- mov dl,dh
- xor dh,dh
- mov bits,dx
- mov bits+2,ax
- sub bits_held,8 ; Compute new bits held
- jmp short fl1 ; See if more left
- fl2: ret
- flush ENDP
-
- lookup_code PROC NEAR
- push ds ; Save seg reg
- mov ds,hash_seg ; Point to hash table
- call index ; Convert code to address
- mov di,0 ; Flag
- cmp [si].first,-1 ; Has this code been used?
- je gc4 ; Equal means no
- inc di ; Set flag
- mov bx,[si].first ; Get first entry
- gc2: call index ; Convert code to address
- cmp [si].char,al ; Is char the same?
- jne gc3 ; ne means no
- clc ; Success
- mov ax,bx ; Put found code in ax
- pop ds ; Restore seg reg
- ret ; Done
- gc3: cmp [si].next,-1 ; More left with this prefix?
- je gc4 ; Equal means no
- mov bx,[si].next ; Get next code
- jmp short gc2 ; Try again
- gc4: stc ; Not found
- pop ds ; Restore seg reg
- ret ; Done
- lookup_code ENDP
-
- index PROC NEAR
- mov si,bx ; si = bx * 5 (5 byte hash entries)
- shl si,1 ; si = bx * 2 * 2 + bx
- shl si,1
- add si,bx
- ret
- index ENDP
-
- add_code PROC NEAR
- mov bx,free_code ; Get code to use
- push ds ; Point to hash table
- mov ds,hash_seg
- cmp di,0 ; First use of this prefix?
- je ac1 ; Equal means yes
- mov [si].next,bx ; Point last use to new entry
- jmp short ac2
- ac1: mov [si].first,bx ; Point first use to new entry
- ac2: cmp bx,maxmax ; Have we reached code limit?
- je ac3 ; Equal means yes, just return
- call index ; Get address of new entry
- mov [si].first,-1 ; Initialize pointers
- mov [si].next,-1
- mov [si].char,al ; Save suffix char
- inc es:free_code ; Adjust next code
- ac3: pop ds ; Restore seg reg
- ret
- add_code ENDP
-
- _TEXT ENDS
-
- END