home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
arc_lbr
/
mdcd10.arc
/
MDCD1213.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-10-26
|
61KB
|
1,221 lines
;---- 10/26/1988 00:26:45 ----}
name mdcd1213
page 82,132
title 'MDCD1213.OBJ 12/13 bit LZW file compress/decompress
;-----------------------------------------------------------------------------
; -
; `MDCD1213' -
; -
; This is a modified version of LZ.ARC, obtained from the EXEC PC BBS -
; in Milwaukee WI. on 8/20/88. It has been changed to work as an external -
; modlule and has been tested with Turbo Pascal 5.0. -
; -
; It was assembled with Turbo Assembler 1.0 but will assemble with MASM. -
; but will assemble with MASM. The following modifications have been made: -
; -
; 1. Removed the malloc memory allocation for the hash table out to -
; the caller so that it can be allocated from the heap. -
; 2. Added the CompressFile & DecompressFile function interface. -
; 3. Minor cleanup and use of equ values for easy buffer size changes. -
; 4. Initialized DS variables normally done by MASM/LINK, but not by some -
; other HLL's (e.g. Turbo Pascal). -
; 5. Input file & output file allocated and opened externally by caller. -
; Only the file handles are passed to this module. -
; 6. Super fast CRC 16 (communications type) routine added to accumulate -
; the compressed & decompressed file's CRC for reliability. -
; 7. Original program used a macro file. The few that were used were -
; removed and coded inline for single module simplicity. -
; 8. Increased disk input and output buffers to 8192 bytes each -
; (from 1024). This seems to be a good trade-off for speed/memory. -
; 9. Moved the disk input and output buffers to CS to preserve precious -
; Turbo Pascal DS space. -
; 10. Combined LZCOMP.ASM & LZDCMP.ASM into 1 module -
; 11. Allowed the beginning file offset to write to and read from to be -
; specified to provide for (n) files in a compressed file. -
; 12. Replaced DIVide instructions with shifts, ands and moves. -
; 13. Added 13 bit LZW compression based on passed parameter. -
; -
; Modified by.. -
; Mike Davenport -
; Mike Davenport & Associates -
; 6751 N. Blackstone Ave. Suite 252 -
; Fresno CA 93710 -
; Voice: (209) 298-8846 -
; CIS: 76676,1362 -
; PLINK: MIKE D -
; GENIE: MDAVENPORT -
; Amiga Techniques + PC Tech BBS - (GT POWER) -
; (209) 298-8453 - 1200-9600 HST (24 hours) -
; -
; Original Author.. -
; Tom Pfau -
; Digital Equipment Corporation -
; Parsippany, NJ -
; -
;-----------------------------------------------------------------------------
; -
; --------------------- -
; MODULE CHANGE HISTORY -
; --------------------- -
; -
; change# ..date.. by .................. change ........................ -
; -
; 000 09-06-88 md Original module creation -
; 001 10-19-88 md added 13 bit LZW compression -
; 001 10-26-88 md Original module finished -
; -
;-----------------------------------------------------------------------------
;----------
; constants
;----------
clear equ 256 ;Clear code
eof equ 257 ;End of file marker
fhdr_len equ 128 ;size of file header for file type 1
first_free equ 258 ;First free code
;NOTE: this module will not handle buffers larger than 8k due to the design
; of the original code. It carries the total bits in the buffer waiting
; to be written to disk in a word then divides by 8 to compare bytes.
; Buffers > 8192 will cause the WORD that holds the total bits to
; overflow. I decided not to mess with this because buffers > 4096
; didn't seem to have a worthwhile memory vs. speed tradeoff that
; justified the non-trivial changes. In other words, I'm lazy!
input_data_size equ 8192 ;input data buffer size
input_data_chk equ 8187 ;size to check end of input coming up
output_data_size equ 8192 ;output data buffer size
output_data_chk equ 8188 ;size to check output buffer write
;--------------------------
; compress 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
;----------------------------
; decompress hash table entry
;----------------------------
dhash_rec struc
dnext dw ? ; prefix code
dchar db ? ; suffix char
dhash_rec ends
;------------------------------ DATA SEGMENT ---------------------------------
data segment word
; data will be initialized upon entry into CompressFile or DecompressFile
; because some HLL's do not do this as does MASM/LINK.
; original LZCOMP.ASM data
bit_offset dw ? ;
free_code dw ? ;
hash_seg dw ? ;segment of hash table from caller
input_handle dw ? ;input file handle from caller
input_offset dw 0 ;offset of next byte to read for input
input_size dw 0 ;number of input bytes read
k db ? ;
max_code dw ? ;
nbits dw ? ;number of bits for current code size
output_handle dw ? ;output file handle from caller
prefix_code dw ? ;
; original data unique to LZDCMP.ASM
dhash_seg dw ?
cur_code dw ?
old_code dw ?
in_code dw ?
stack_count dw 0
dmax_code dw 512 ;hash table bytes for 9 bit codes
fin_char db ?
masks dw 1ffh,3ffh,7ffh,0fffh,0ffffh ;9-13 bit masks
dbit_offset dw 0
output_offset dw 0
; miscellaneous variables
maxmax dw ? ;max hash table bytes (12/13 bit)
maxbits dw ? ;max bits for lzw (12 or 13)
crc dw ? ;file crc
byte_hi dw ? ;hi order byte of double word lseek
byte_lo dw ? ;lo order byte of double word lseek
rtn_seg dw ? ;return address segment
rtn_ofs dw ? ;return address offset
totalbytes1 dw ? ;total file bytes written low
totalbytes2 dw ? ;total file bytes written high
rc dw ? ;return code for caller
data ends
;------------------------------ CODE SEGMENT ---------------------------------
code segment word
assume cs:code, ds:data, es:data, ss:nothing
PUBLIC CompressFile
PUBLIC DeCompressFile
; disk i/o buffers & CRC table are in CS to preserve DS space
input_data db 8192 dup (?) ;disk input buffer
output_data db 8192 dup (?) ;disk output buffer
; Machine Code with CRC Table 256 - CRC Polynomial Divisor = $11021
crctab dw 00000h, 01021h, 02042h, 03063h, 04084h, 050A5h, 060C6h, 070E7h
dw 08108h, 09129h, 0A14Ah, 0B16Bh, 0C18Ch, 0D1ADh, 0E1CEh, 0F1EFh
dw 01231h, 00210h, 03273h, 02252h, 052B5h, 04294h, 072F7h, 062D6h
dw 09339h, 08318h, 0B37Bh, 0A35Ah, 0D3BDh, 0C39Ch, 0F3FFh, 0E3DEh
dw 02462h, 03443h, 00420h, 01401h, 064E6h, 074C7h, 044A4h, 05485h
dw 0A56Ah, 0B54Bh, 08528h, 09509h, 0E5EEh, 0F5CFh, 0C5ACh, 0D58Dh
dw 03653h, 02672h, 01611h, 00630h, 076D7h, 066F6h, 05695h, 046B4h
dw 0B75Bh, 0A77Ah, 09719h, 08738h, 0F7DFh, 0E7FEh, 0D79Dh, 0C7BCh
dw 048C4h, 058E5h, 06886h, 078A7h, 00840h, 01861h, 02802h, 03823h
dw 0C9CCh, 0D9EDh, 0E98Eh, 0F9AFh, 08948h, 09969h, 0A90Ah, 0B92Bh
dw 05AF5h, 04AD4h, 07AB7h, 06A96h, 01A71h, 00A50h, 03A33h, 02A12h
dw 0DBFDh, 0CBDCh, 0FBBFh, 0EB9Eh, 09B79h, 08B58h, 0BB3Bh, 0AB1Ah
dw 06CA6h, 07C87h, 04CE4h, 05CC5h, 02C22h, 03C03h, 00C60h, 01C41h
dw 0EDAEh, 0FD8Fh, 0CDECh, 0DDCDh, 0AD2Ah, 0BD0Bh, 08D68h, 09D49h
dw 07E97h, 06EB6h, 05ED5h, 04EF4h, 03E13h, 02E32h, 01E51h, 00E70h
dw 0FF9Fh, 0EFBEh, 0DFDDh, 0CFFCh, 0BF1Bh, 0AF3Ah, 09F59h, 08F78h
dw 09188h, 081A9h, 0B1CAh, 0A1EBh, 0D10Ch, 0C12Dh, 0F14Eh, 0E16Fh
dw 01080h, 000A1h, 030C2h, 020E3h, 05004h, 04025h, 07046h, 06067h
dw 083B9h, 09398h, 0A3FBh, 0B3DAh, 0C33Dh, 0D31Ch, 0E37Fh, 0F35Eh
dw 002B1h, 01290h, 022F3h, 032D2h, 04235h, 05214h, 06277h, 07256h
dw 0B5EAh, 0A5CBh, 095A8h, 08589h, 0F56Eh, 0E54Fh, 0D52Ch, 0C50Dh
dw 034E2h, 024C3h, 014A0h, 00481h, 07466h, 06447h, 05424h, 04405h
dw 0A7DBh, 0B7FAh, 08799h, 097B8h, 0E75Fh, 0F77Eh, 0C71Dh, 0D73Ch
dw 026D3h, 036F2h, 00691h, 016B0h, 06657h, 07676h, 04615h, 05634h
dw 0D94Ch, 0C96Dh, 0F90Eh, 0E92Fh, 099C8h, 089E9h, 0B98Ah, 0A9ABh
dw 05844h, 04865h, 07806h, 06827h, 018C0h, 008E1h, 03882h, 028A3h
dw 0CB7Dh, 0DB5Ch, 0EB3Fh, 0FB1Eh, 08BF9h, 09BD8h, 0ABBBh, 0BB9Ah
dw 04A75h, 05A54h, 06A37h, 07A16h, 00AF1h, 01AD0h, 02AB3h, 03A92h
dw 0FD2Eh, 0ED0Fh, 0DD6Ch, 0CD4Dh, 0BDAAh, 0AD8Bh, 09DE8h, 08DC9h
dw 07C26h, 06C07h, 05C64h, 04C45h, 03CA2h, 02C83h, 01CE0h, 00CC1h
dw 0EF1Fh, 0FF3Eh, 0CF5Dh, 0DF7Ch, 0AF9Bh, 0BFBAh, 08FD9h, 09FF8h
dw 06E17h, 07E36h, 04E55h, 05E74h, 02E93h, 03EB2h, 00ED1h, 01EF0h
; save area for critical registers so we can unwind and return to caller
; in the event of a severe error
savebp dw ? ;save bp
savesp dw ? ;save sp
savess dw ? ;save ss
saveds dw ? ;save ds
saveparms dw ? ;number of stack bytes for parms
;---------------------------------------------------------------------------;
; ;
; COMPRESSION ROUTINES ;
; ;
; 'Compress' ;
; ;
;---------------------------------------------------------------------------;
compress proc near
l1: call cinit_table ;Initialize the table and some vars
mov ax,clear ;Write a clear code
call write_code
call read_char ;Read first char
l4: xor ah,ah ;Turn char into code
l4a: mov prefix_code,ax ;Set prefix code
call read_char ;Read next char
jc l17 ;Carry means eof
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
mov cx,maxbits ;get maxbits for compare
cmp nbits,cx ;Currently less than (12 or 13) bits?
jl l14 ;yes
mov ax,clear ;Write a clear code
call write_code
call cinit_table ;Reinit table
mov al,k ;get last char
jmp l4 ;Start over
l14: inc nbits ;Increase number of bits
shl max_code,1 ;Double max code size
jmp l4 ;Get next char
l17: mov ax,prefix_code ;Write last code
call write_code
mov ax,eof ;Write eof code
call write_code
mov ax,bit_offset ;Make sure buffer is flushed to file
cmp ax,0
je l18
; convert bits to bytes
mov dx,ax ;get all of ax
and dx,07h ;simulate remainder modulo 8
shr ax,1 ;divide ax by 8 (2)
shr ax,1 ; " " (4)
shr ax,1 ; " " (8)
or dx,dx ;If extra bits, make sure they get
je l17a ;written
inc ax
l17a: call flush
l18: ret
compress endp
;---------------------------------------------------------------------------;
; ;
; 'cinit_table' ;
; ;
;---------------------------------------------------------------------------;
cinit_table proc near
mov nbits,9 ;Set code size to start at 9 bits
mov max_code,512 ;Set max code to 512
push es ;Save seg reg
mov es,hash_seg ;Address hash table
mov ax,-1 ;Unused flag
mov cx,640 ;Clear first 256 entries (256*5/2 words)
mov di,0 ;Point to begin of hash table
rep stosw ;Clear it out
pop es ;Restore seg reg
mov free_code,first_free ;Set next code to use
ret ;done
cinit_table endp
;---------------------------------------------------------------------------;
; ;
; 'write_code' ;
; ;
;---------------------------------------------------------------------------;
write_code proc near
push ax ;Save code
mov ax,bit_offset ;Get bit offset
mov cx,nbits ;Adjust bit offset by code size
add bit_offset,cx
;
; the five following instructions are the same as:
;
;;;; mov cx,8 ;Convert bit offset to byte offset
;;;; xor dx,dx
;;;; div cx
;
; the above instructions take approximately 170 cycles, the 5 instructions
; below take 12 cycles. These instructions divide the contents of ax by 8
; and store the remainder in dx. This is done for speeeeeed ! For anyone
; interested, I ran some timings on some LARGE files and it did not make
; a measurable difference in time. Oh well.. I just hate seeing DIV
; instructions in often repeated routines.
;
mov dx,ax ;get all of ax
and dx,07h ;simulate remainder modulo 8
shr ax,1 ;divide ax by 8 (2)
shr ax,1 ; " " (4)
shr ax,1 ; " " (8)
cmp ax,output_data_chk ;Approaching end of buffer?
jl wc1 ;less means no
call flush ;Write the buffer
push dx ;dx contains offset within byte
add dx,nbits ;adjust by code size
mov bit_offset,dx ;new bit offset
pop dx ;restore dx
add ax,offset output_data ;Point to last byte
mov si,ax ;put in si
mov al,cs:byte ptr [si] ;move byte to first position
mov cs:output_data,al
xor ax,ax ;Byte offset of zero
wc1: add ax,offset output_data ;Point into buffer
mov di,ax ;Destination
pop ax ;Restore code
push es ;save es
mov cx,cs ;mov es,cs to reference disk output
mov es,cx ;... buffer in cs for stosw/stosb
mov cx,dx ;offset within byte
xor dx,dx ;dx will catch bits rotated out
jcxz wc3 ;If offset in byte is zero, skip shift
wc2: shl ax,1 ;Rotate code
rcl dx,1
loop wc2
or al,cs:byte ptr [di] ;Grab bits currently in buffer
wc3: stosw ;Save data
mov al,dl ;Grab extra bits
stosb ;and save
pop es ;restore es
ret
write_code endp
;---------------------------------------------------------------------------;
; ;
; 'flush' ;
; ;
;---------------------------------------------------------------------------;
flush proc near
push ax ;Save all registers
push bx ;AX contains number of bytes to write
push cx
push dx
; write data to disk
add totalbytes1,ax ;accumulate total bytes written
jnc noof3 ;if no carry, skip
inc totalbytes2 ;else increment for long add
noof3: ;
mov bx,output_handle ;setup output file handle
push ds ;save ds
mov cx,cs ;mov ds,cs to reference disk output
mov ds,cx ;... buffer in cs when calling dos
lea dx,output_data ;address of output buffer
mov cx,ax ;number of bytes to write
mov ah,40h ;dos function - write to file/device
int 21h ;call dos
jnc i21ok1 ;if no disk error, continue
jmp diskerror ;else report disk error and exit
i21ok1:
pop ds ;restore ds
pop dx ;restore saved registers
pop cx
pop bx
pop ax
ret
flush endp
;---------------------------------------------------------------------------;
; ;
; 'read_char' ;
; ;
;---------------------------------------------------------------------------;
read_char proc near
mov di,input_offset ;Anything left in buffer?
cmp di,input_size
jl rd1 ;less means yes
; read data from disk
mov bx,input_handle ;setup input file handle
push ds ;save ds
mov dx,cs ;mov ds,cs to reference disk input
mov ds,dx ;... buffer in cs when calling dos
lea dx,input_data ;offset of input buffer
mov cx,input_data_size ;max bytes to read
mov ah,3fh ;dos function - read from file/device
int 21h ;call dos
jnc i21ok2 ;if no disk error, continue
jmp diskerror ;else report disk error and exit
i21ok2:
pop ds ;restore ds
cmp ax,0 ;Anything left?
je rd2 ;equal means no, finished
mov input_size,ax ;Save bytes read
mov input_offset,0 ;Point to beginning of buffer
mov di,0
rd1:
lea bx,input_data[di] ;Point at character
mov al,cs:[bx] ;get next file character into al
; the following CRC16 (ala Xmodem crc) code is inline for max speeeeeeed...!
mov dx,crc ;get old crc
xchg dl,dh ;swap crc bytes
xor ah,ah ;make sure ah is zero (al has the data)
xor dx,ax ;xor low crc with new data byte
xor bx,bx ;clear bx
xchg dl,bl ;swap low crc to bl
shl bx,1 ;bx = bx * 2
xor dx,cs:[crctab+bx] ;xor crc with table value
mov crc,dx ;store updated crc
inc input_offset ;Adjust pointer
clc ;Success
ret
rd2: stc ;Nothing left
ret
read_char endp
;---------------------------------------------------------------------------;
; ;
; 'lookup_code' ;
; ;
;---------------------------------------------------------------------------;
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 gc2 ;try again
gc4: stc ;not found
pop ds ;restore seg reg
ret ;done
lookup_code endp
;---------------------------------------------------------------------------;
; ;
; 'index' ;
; ;
;---------------------------------------------------------------------------;
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' ;
; ;
;---------------------------------------------------------------------------;
add_code proc near
mov bx,free_code ;Get code to use
push ds ;point to hash table
mov ds,hash_seg ;get segment of heap hash table
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
;---------------------------------------------------------------------------;
; ;
; function 'CompressFile' ;
; ;
; Function interface to other languages for file compression ;
; ;
; See TESTC.PAS for an example ;
; ;
; inhandle & outhandle must be DOS handles of already opened files ;
; ;
; bytehi & bytelo is a long integer specifying the starting byte offset ;
; of where to start reading the input file ;
; ;
; rtnseg & rtnofs is a pointer to a return area where this module will ;
; pass back the CRC and the compressed file size ;
; ;
; hashsegment is the segment:0 address of memory allocated by the caller. ;
; if you are requesting 12 bit compression, this must point;
; to 20480 bytes of available memory. If you are requesting;
; 13 bit, it must point to 40960 bytes of available memory ;
; ;
; lzwbits must be a word value of 12 for 12 bit compression or 13 for 13 ;
; bit compression ;
; ;
;---------------------------------------------------------------------------;
inhandle equ word ptr [bp+20] ;input handle on stack
outhandle equ word ptr [bp+18] ;output handle on stack
bytehi equ word ptr [bp+16] ;byte offset (hi order)
bytelo equ word ptr [bp+14] ;byte offset (low order)
rtnseg equ word ptr [bp+12] ;segment of return record
rtnofs equ word ptr [bp+10] ;offset of return record
hashsegment equ word ptr [bp+8] ;segment of hash table
lzwbits equ word ptr [bp+6] ;number of bits (lzw) 12 or 13
cstack equ 16 ;number of bytes passed on stack
;---------------------------------------------------------------------------;
; ;
; (FAR) 'CompressFile' ;
; ;
;---------------------------------------------------------------------------;
CompressFile proc far
; save bp, sp, ss, ds, and stack parameter count so we can exit this module
; in the event of an error (e.g. disk i/o)
mov cs:savebp,bp ;save bp
mov cs:savesp,sp ;save sp
mov cs:savess,ss ;save ss
mov cs:saveds,ds ;save ds
mov cs:saveparms,cstack ;number of stack bytes for parms
; set up reference to callers parameters and save anything required
push bp ;save bp
mov bp,sp ;set stack frame to reference my parms
push ds ;save ds
; set up ds & es addressability
mov bx,data ;Set up data segment addressability
mov es,bx ;
mov ds,bx ;
; get parameters from caller
mov ax,inhandle ;store input file handle
mov input_handle,ax ;
mov ax,outhandle ;store output file handle
mov output_handle,ax ;
mov ax,bytehi ;store hi order byte offset
mov byte_hi,ax ;
mov ax,bytelo ;store lo order byte offset
mov byte_lo,ax ;
mov ax,rtnseg ;store return address segment
mov rtn_seg,ax ;
mov ax,rtnofs ;store return address offset
mov rtn_ofs,ax ;
mov ax,hashsegment ;store seg pointer to heap hash table
mov hash_seg,ax ;
mov cx,lzwbits ;store lzw type (12 or 13)
mov maxbits, cx ;
mov maxmax,1 ;calc max entries for lzw type
shl maxmax,cl ; (12 bit = 4096, 13 bit = 8192)
; initialize anything required
mov input_offset,0 ;clear input_offset
mov input_size,0 ;clear input_size
mov bit_offset,0 ;clear bit_offset
mov crc,0 ;clear crc for new file
mov totalbytes1,0 ;clear bytes written count
mov totalbytes2,0 ;
cld ;clear direction flag for di & si
; move file pointer so that writes start at the requested byte
mov bx,output_handle ;setup output file handle
mov cx,byte_hi ;hi-order byte offset to start write
mov dx,byte_lo ;lo-order byte offset to start write
mov ah,42h ;dos function - move file pointer
mov al,00h ; method = absolute from beginning
int 21h ;call dos
jnc i21ok3 ;if no disk error, continue
jmp diskerror ;else report disk error and exit
i21ok3:
; compress the file
call compress ;Compress file
; wrap it up
mov bx,rtn_ofs ;setup offset of callers return area
mov ds,rtn_seg ;setup segment of callers return area
mov ax,crc ;send back crc
mov [bx],ax ;
mov ax,totalbytes1 ;send back low order total bytes
mov [bx]+2,ax ;
mov ax,totalbytes2 ;send back high order total bytes
mov [bx]+4,ax ;
mov rc,-1 ;good return code
mov ax,rc ;function return = return code
pop ds ;restore ds
pop bp ;restore bp
ret cstack ;clean parms from stack-back to caller
CompressFile endp
;---------------------------------------------------------------------------;
; ;
; DECOMPRESSION ROUTINES ;
; ;
; 'DeCompress' ;
; ;
;---------------------------------------------------------------------------;
decompress proc near
mov bx,input_handle ;setup input file handle
push ds ;save ds
mov dx,cs ;mov ds,cs to reference disk input
mov ds,dx ;... buffer in cs when calling dos
lea dx,input_data ;offset of input buffer
mov cx,input_data_size ;max bytes to read
mov ah,3fh ;dos function - read from file/device
int 21h ;call dos
jnc i21ok4 ;if no disk error, continue
jmp diskerror ;else report disk error and exit
i21ok4:
pop ds ;restore ds
z1: call read_code ;Get a code
cmp ax,eof ;End of file?
jne z2 ;no
cmp output_offset,0 ;Data in output buffer?
je z1a ;no
;write data to disk
mov bx,output_handle ;setup output file handle
mov cx,output_offset ;number of bytes to write
add totalbytes1,cx ;accumulate total bytes written
jnc noof1 ;if no carry, skip
inc totalbytes2 ;else increment for long add
noof1: ;
push ds ;save ds
mov dx,cs ;mov ds,cs to reference disk output
mov ds,dx ;... buffer in cs when calling dos
lea dx,output_data ;address of output buffer
mov ah,40h ;dos function - write to file/device
int 21h ;call dos
jnc i21ok5 ;if no disk error, continue
jmp diskerror ;else report disk error and exit
i21ok5:
pop ds ;restore ds
z1a: ret ;done
z2: cmp ax,clear ;Clear code?
jne z7 ;no
call init_tab ;Initialize table
call read_code ;Read next code
mov cur_code,ax ;Initialize variables
mov old_code,ax
mov k,al
mov fin_char,al
mov al,k
call write_char ;Write character
jmp z1 ;Get next code
z7: mov cur_code,ax ;Save new code
mov in_code,ax
mov es,dhash_seg ;Point to hash table
cmp ax,free_code ;Code in table? (k<w>k<w>k)
jl z11 ;yes
mov ax,old_code ;get previous code
mov cur_code,ax ;make current
mov al,fin_char ;get old last char
push ax ;push it
inc stack_count
z11: cmp cur_code,255 ;Code or character?
jle z15 ;Char
mov bx,cur_code ;Convert code to address
call dindex
mov al,es:2[bx] ;Get suffix char
push ax ;push it
inc stack_count
mov ax,es:[bx] ;Get prefix code
mov cur_code,ax ;Save it
jmp z11 ;Translate again
z15: mov ax,ds ;Restore seg reg
mov es,ax
mov ax,cur_code ;Get code
mov fin_char,al ;Save as final, k
mov k,al
push ax ;Push it
inc stack_count
mov cx,stack_count ;Pop stack
jcxz z18 ;If anything there
z17: pop ax
call write_char
loop z17
z18: mov stack_count,cx ;Clear count on stack
call dadd_code ;Add new code to table
mov ax,in_code ;Save input code
mov old_code,ax
mov bx,free_code ;Hit table limit?
cmp bx,dmax_code
jl z23 ;Less means no
mov cx,maxbits ;get maxbits for compare
cmp nbits,cx ;Currently less than (12 or 13) bits?
je z23 ;no (next code should be clear)
inc nbits ;Increase code size
shl dmax_code,1 ;Double max code
z23: jmp z1 ;Get next code
decompress endp
;---------------------------------------------------------------------------;
; ;
; 'read_code' ;
; ;
;---------------------------------------------------------------------------;
read_code proc near
mov ax,dbit_offset ;Get bit offset
add ax,nbits ;Adjust by code size
xchg dbit_offset,ax ;Swap
; convert bits to bytes
mov dx,ax ;get all of ax
and dx,07h ;simulate remainder modulo 8
shr ax,1 ;divide ax by 8 (2)
shr ax,1 ; " " (4)
shr ax,1 ; " " (8)
cmp ax,input_data_chk ;Approaching end of buffer?
jl xd0 ;no
push dx ;Save offset in byte
add dx,nbits ;Calculate new bit offset
mov dbit_offset,dx
mov cx,input_data_size ;buffer size
mov bp,ax ;save byte offset
sub cx,ax ;Calculate bytes left
; make the following buffer shuffle, and read code, segment relative
mov bx,input_handle ;setup input handle while ds still ok
push ds ;Save ds
push es ;Save es
mov dx,cs ;Set ds & es to cs
mov ds,dx ; "
mov es,dx ; "
; move unprocessed input buffer down to the beginning of the buffer
add ax,offset input_data ;Point to char
mov si,ax
lea di,input_data ;Point to beginning of buffer
rep movsb ;Move last chars down
; fill up the rest of the input buffer
lea dx,[di] ;offset of input buffer
mov cx,bp ;bytes to read
mov ah,3fh ;dos function - read from file/device
int 21h ;call dos
jnc i21ok6 ;if no disk error, continue
jmp diskerror ;else report disk error and exit
i21ok6:
pop es ;restore es
pop ds ;restore ds
xor ax,ax ;Clear ax
pop dx ;Restore offset in byte
xd0:
push ds ;save ds
mov bx,cs ;ds = cs (to reference buffer in cs)
mov ds,bx ; " "
add ax,offset input_data ;Point to char
mov si,ax
lodsw ;Get word
mov bx,ax ;Save in AX
lodsb ;Next byte
pop ds ;restore ds
mov cx,dx ;Offset in byte
jcxz xd2 ;If zero, skip shifts
xd1: shr al,1 ;Put code in low (code size) bits of BX
rcr bx,1
loop xd1
xd2: mov ax,bx ;put code in ax
mov bx,nbits ;mask off unwanted bits
sub bx,9 ;9 bits = masks[0] 10 bits = masks[2]
shl bx,1 ; etc. etc.
and ax,masks[bx] ;
ret
read_code endp
;---------------------------------------------------------------------------;
; ;
; 'init_tab' ;
; ;
;---------------------------------------------------------------------------;
init_tab proc near
mov nbits,9 ;Initialize variables
mov dmax_code,512
mov free_code,first_free
ret
init_tab endp
;---------------------------------------------------------------------------;
; ;
; 'write_char' ;
; ;
;---------------------------------------------------------------------------;
write_char proc near
mov di,output_offset ;Get offset in buffer
cmp di,output_data_size ;Full?
jl wch1 ;no
push ax ;Save registers
push cx
mov bx,output_handle ;setup output file handle
add totalbytes1,di ;accumulate total bytes written
jnc noof2 ;if no carry, skip
inc totalbytes2 ;else increment for long add
noof2: ;
push ds ;save ds
mov cx,cs ;mov ds,cs to reference disk output
mov ds,cx ;... buffer in cs when calling dos
lea dx,output_data ;address of output buffer
mov cx,di ;number of bytes to write
mov ah,40h ;dos function - write to file/device
int 21h ;call dos
jnc i21ok7 ;if no disk error, continue
jmp diskerror ;else report disk error and exit
i21ok7:
pop ds ;restore ds
pop cx ;restore cx
pop ax ;restore ax
mov di,0 ;Point to beginning of buffer
mov output_offset,di ;reset buffer offset to 0
wch1:
push es ;save es
mov dx,cs ;es=cs so es:di points to buffer in cs
mov es,dx
lea di,output_data[di] ;Point into buffer
stosb ;Store char
pop es
; the following CRC16 (ala Xmodem crc) code is inline for max speeeeeeed...!
push cx ;save cx (loop counter in decompress)
mov dx,crc ;get old crc
xchg dl,dh ;swap crc bytes
xor ah,ah ;make sure ah is zero (al has the data)
xor dx,ax ;xor low crc with new data byte
xor bx,bx ;clear bx
xchg dl,bl ;swap low crc to bl
shl bx,1 ;bx = bx * 2
xor dx,cs:[crctab+bx] ;xor crc with table value
mov crc,dx ;store updated crc
pop cx ;restore cx
inc output_offset ;Increment number of chars in buffer
ret
write_char endp
;---------------------------------------------------------------------------;
; ;
; 'dindex' ;
; ;
;---------------------------------------------------------------------------;
dindex proc near
mov bp,bx ;bx = bx * 3 (3 byte entries)
shl bx,1 ;bp = bx
add bx,bp ;bx = bx * 2 + bp
ret
dindex endp
;---------------------------------------------------------------------------;
; ;
; 'dadd_code' ;
; ;
;---------------------------------------------------------------------------;
dadd_code proc near
mov bx,free_code ;Get new code
call dindex ;convert to address
push es ;point to hash table
mov es,dhash_seg
mov al,k ;get suffix char
mov es:[bx].dchar,al ;save it
mov ax,old_code ;get prefix code
mov es:[bx].dnext,ax ;save it
pop es
inc free_code ;set next code
ret
dadd_code endp
;---------------------------------------------------------------------------;
; ;
; function 'DeCompressFile' ;
; ;
; Function interface to other languages for file decompression ;
; ;
; See TESTD.PAS for an example ;
; ;
; dinhandle & douthandle must be DOS handles of already opened files ;
; ;
; dbytehi & dbytelo is a long integer specifying the starting byte offset ;
; of where to start reading the input file ;
; ;
; drtnseg & drtnofs is a pointer to a return area where this module will ;
; pass back the CRC and the compressed file size ;
; ;
; dhashsegment is the segment:0 address of memory allocated by the caller. ;
; if you are requesting 12 bit decompression, this must point;
; to 12288 bytes of available memory. If you are requesting ;
; 13 bit, it must point to 24576 bytes of available memory ;
; ;
; dlzwbits must be a word value of 12 for 12 bit decompression or 13 for ;
; 13 bit decompression ;
; ;
;---------------------------------------------------------------------------;
dinhandle equ word ptr [bp+20] ;input handle on stack
douthandle equ word ptr [bp+18] ;output handle on stack
dbytehi equ word ptr [bp+16] ;byte offset (hi order)
dbytelo equ word ptr [bp+14] ;byte offset (low order)
drtnseg equ word ptr [bp+12] ;segment of return record
drtnofs equ word ptr [bp+10] ;offset of return record
dhashsegment equ word ptr [bp+8] ;segment of hash table on stack
dlzwbits equ word ptr [bp+6] ;number of bits (lzw) 12 or 13
dstack equ 16 ;number of bytes passed on stack
;---------------------------------------------------------------------------;
; ;
; (FAR) 'DeCompressFile' ;
; ;
;---------------------------------------------------------------------------;
DeCompressFile proc far
; save bp, sp, ss, ds, and stack parameter count so we can exit this module
; in the event of an error (e.g. disk i/o)
mov cs:savebp,bp ;save bp
mov cs:savesp,sp ;save sp
mov cs:savess,ss ;save ss
mov cs:saveds,ds ;save ds
mov cs:saveparms,dstack ;number of stack bytes for parms
; set up reference to callers parameters and save anything required
push bp ;save bp
mov bp,sp ;set stack frame to reference my parms
push ds ;save ds
; set up ds & es addressability
mov bx,data ;Set up data segment addressability
mov es,bx ;
mov ds,bx ;
; get parameters from caller
mov ax,dinhandle ;store input file handle
mov input_handle,ax ;
mov ax,douthandle ;store output file handle
mov output_handle,ax ;
mov ax,dbytehi ;store hi order byte offset
mov byte_hi,ax ;
mov ax,dbytelo ;store lo order byte offset
mov byte_lo,ax ;
mov ax,drtnseg ;store return address segment
mov rtn_seg,ax ;
mov ax,drtnofs ;store return address offset
mov rtn_ofs,ax ;
mov ax,dhashsegment ;store seg pointer to heap hash table
mov dhash_seg,ax ;
mov cx,dlzwbits ;store lzw type (12 or 13)
mov maxbits, cx ;
mov maxmax,1 ;calc max entries for lzw type
shl maxmax,cl ; (12 bit = 4096, 13 bit = 8192)
; initialize anything required
mov input_offset,0 ;clear input_offset
mov output_offset,0 ;clear output_offset
mov input_size,0 ;clear input_size
mov dbit_offset,0 ;clear input_size
mov stack_count,0 ;clear stack_count
mov totalbytes1,0 ;clear bytes written read count
mov totalbytes2,0 ;
mov crc,0 ;clear crc for new file
mov nbits,9 ;set number of starting bits
mov free_code,first_free ;set first free code
mov dmax_code,512 ;set first free code
mov masks+0,1ffh ;set up mask for 9 bit codes
mov masks+2,3ffh ;set up mask for 10 bit codes
mov masks+4,7ffh ;set up mask for 11 bit codes
mov masks+6,0fffh ;set up mask for 12 bit codes
mov masks+8,1fffh ;set up mask for 13 bit codes
cld ;clear direction flag for di & si
; move file pointer so that reads start at the requested byte
mov bx,input_handle ;setup intput file handle
mov cx,byte_hi ;hi-order byte offset to start write
mov dx,byte_lo ;lo-order byte offset to start write
mov ah,42h ;dos function - move file pointer
mov al,00h ; method = absolute from beginning
int 21h ;call dos
jnc i21ok8 ;if no disk error, continue
jmp diskerror ;else report disk error and exit
i21ok8:
; decompress the file
call decompress ;DeCompress file
; wrap it up
mov bx,rtn_ofs ;setup offset of callers return area
mov ds,rtn_seg ;setup segment of callers return area
mov ax,crc ;send back crc
mov [bx],ax ;
mov ax,totalbytes1 ;send back low order total bytes
mov [bx]+2,ax ;
mov ax,totalbytes2 ;send back high order total bytes
mov [bx]+4,ax ;
mov rc,-1 ;good return code
mov ax,rc ;function return = return code
pop ds ;restore ds
pop bp ;restore bp
ret dstack ;clean parms from stack-back to caller
DeCompressFile endp
;---------------------------------------------------------------------------;
; ;
; 'diskerror' ;
; ;
; clear screen, display an error message, restore the routine entry state ;
; and exit gracefully back to caller with a function return code of (0) ;
; (error). ;
; ;
; This routine is entered via a jump, not a call. ;
; ;
;---------------------------------------------------------------------------;
diskerror proc near
jmp skipdt ;bypass data
; don't change the order or position of the following jmp.... definitions
jmpofst dw 0 ;offset for far jump return to TP
jmpseg dw 0 ;segment for far jump return to TP
; error message to display if this routine is called
errmsg db 0dh, 0ah, 0ah, 07h
db 'SEVERE DISK I/O ERROR IN MODULE: '
db 'MDCD1213.OBJ COMPRESS/DECOMPRESS'
db 0dH, 0aH, 0ah
db 'CLEANING UP AND RETURNING TO CALLING ROUTINE'
db 0dh, 0ah, 0ah, 07h, '$'
db '$'
skipdt:
; get the screen attribute currently at cursor and clear screen
xor bh,bh ;video page 0
mov ah,08h ;bios video function = read attr/char
int 10h ;call bios video
mov bh,ah ;save attribute for function 6 call
mov ah,06h ;video function = scroll page up
xor al,al ;blank screen
xor cx,cx ;clear screen (scroll) from 0,0 to..
mov dx,184fh ; ... to 24,79
int 10h ;call bios video
; display error message
mov ah,09h ;dos function = display string
lea dx,errmsg ;address of data to display
push cs ;make ds point to code segment
pop ds ; " "
int 21h ;call dos
; reset all of callers stuff so we can get back gracefully
mov bp,cs:savebp ;restore tp's bp
mov ds,cs:saveds ;restore tp's ds
cli ;interrupts off for old 8088 chips
mov sp,cs:savesp ;restore entry sp
mov ss,cs:savess ;restore entry ss (shud be the same)
sti ;interrupts back on
; the following code pulls the callers return address (segment:offset) off
; from the stack, adjusts the stack, and returns to the caller
pop cs:jmpofst ;pull return address off of stack
pop cs:jmpseg ;pull return segment off of stack
add sp,cs:saveparms ;get rid of locals from stack
xor ax,ax ;error return code for caller
jmp dword ptr cs:jmpofst ;return far to caller
diskerror endp
code ends
end