home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
pcmagazi
/
1989
/
21
/
dcompres.asm
< prev
next >
Wrap
Assembly Source File
|
1989-10-31
|
31KB
|
1,493 lines
.MODEL small
cseg segment para public 'CODE'
assume cs:cseg
;;;;;;;;;;;;;;;;;
;;
;; DCOMPRES - Keeps track of last access time and date for a file,
;; decompresses already compressed files when accessed
;; Copyright (c) 1989 by Ziff Communications Co.
;; Program by Ross M. Greenberg
;;;;;;;;;;;;;;;;;
org 100h
TRUE equ 1
FALSE equ 0
NULL equ 0
CR equ 0dh
LF equ 0ah
BELL equ 07h
start:
jmp install ; It's traditional!
MAX_FILES equ 100 ; max files in index
FN_SIZE equ 14 ; filename + dot + extension
MAX_FN_SIZE equ 64 + FN_SIZE ; and the maximum path
;;;;;;;;;;;;;;;;;
;; this structure should be an even number of bytes or contortions in C will
;; occur
;;;;;;;;;;;;;;;;
file struc
filename_len dw 0
filename db FN_SIZE dup (0)
left_ptr dw 0
right_ptr dw 0
date dw 2 dup (0)
time dw 2 dup (0)
status db 0 ;normal = 0, compressed = 1 , del = 2
access_cnt db 0 ; extra byte. Why not?
file ends
BUF_SIZE equ size file * MAX_FILES
NORMAL equ 0
COMPRESSED equ 1
DELETED equ 2
num_files dw 0 ;don't separate these three!!!
dir_name2 db MAX_FN_SIZE dup(0)
buffer db BUF_SIZE dup(0)
tag db 0cdh, 020h, 'PCOMPRES' ; don't separate these two
TAG_LEN equ $ - tag
file_out db 'PCOMPRES.$$$', 0 ; or these two
FILE_OUT_LEN equ $ - file_out
index_name db 'INDEX.CMP',0 ; don;t separate these two
INDEX_NAME_LEN equ $ - index_name
dir_name db MAX_FN_SIZE dup(0)
dir_name3 db MAX_FN_SIZE dup(0)
dir_name4 db MAX_FN_SIZE dup(0)
file_name db FN_SIZE dup(0)
tmp_buffer db MAX_FN_SIZE dup(0)
tmp_buf2 db FN_SIZE dup(0)
dos_handle dw 0
in_handle dw 0
was_compressed dw 0
tmp_len dw 0
my_psp dw 0
users_psp dw 0
index_handle dw 0
file_seg dw 0
file_off dw 0
dirty_bit dw 0
in_use dw 0
switch_off dw 0
last_dos dw 0
old_dx dw 0
ignore_status dw 0
attributes dw 1
old_attrb dw 0
tag_buf db TAG_LEN dup (0)
table struc
cmp_code dw 0
suffix db 0
table ends
MAX_CODE equ 4096
RESET_TABLE equ (MAX_CODE - 1)
codes db (MAX_CODE * size table) dup (0)
which_code dw 0
codes_used dw 0
stack db MAX_CODE dup (0)
s_ptr dw stack
INBUF_SIZE equ 3000
in_buffer db INBUF_SIZE dup (0)
out_buf db INBUF_SIZE dup (0)
out_handle dw 0
out_cnt dw 0
buff_cnt dw 0
buff_size dw 0
tmp db 0
hold dw 0
old_code dw 0
last_char db 0
incode dw 0
screen_msg db 'Decompressing File...Standby'
SCREEN_LEN equ ($ - screen_msg)
screen_attrb db SCREEN_LEN dup(09fh);
screen_segment dw 0b800h
SCREEN_OFFSET equ 2 * ((12 * 80) + (40 - SCREEN_LEN/2))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Function 6c -- new to DOS 4.x is a pain. All other functions operating
;; on files have consistent usage of ds:dx for the pointer to the file name.
;; Not this puppy. Dile name is in ds:si. Makes more sense, but it isn't
;; consistant. This kludge makes it *look* consistent
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
set_6c: cmp byte ptr cs:[last_dos + 1], 06ch
jnz not_6c1
mov cs:[old_dx], dx
mov dx, si
not_6c1:
ret
;;;;;;;;;;;;;;;;;;;;;;;;
;; The reverse of the above
;;;;;;;;;;;;;;;;;;;;;;;;
reset_6c: cmp byte ptr cs:[last_dos + 1], 06ch
jnz not_6c2
mov dx, cs:[old_dx]
not_6c2:
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Routine called by PCMANAGE to turn on and off the COMPRES program,
;; dump out and read back in the file (if one is on memory)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
set_flag:
push ds
push cs
pop ds
mov [switch_off], dx
mov bx, [index_handle] ; close file on change
cmp bx, 0 ; first time?
jz not_yet ; yes
inc [in_use]
push ax
push bx
mov ah, 051h
int 21h
mov [users_psp], bx
mov bx, [my_psp]
mov ah, 050h
int 21h
pop bx
pop ax
call update_file
mov ah, 03eh
int 21h
push cx
push dx
mov ax, 04301h
mov dx, offset dir_name
mov cx, [old_attrb]
int 21h
pop dx
pop cx
mov [dir_name], 0
mov bx, [users_psp]
mov ah, 050h
int 21h
dec [in_use]
not_yet:
pop ds
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; New Dos Interrupt Service Routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
old_dos dw 0 ; first the offset
dw 0 ; then the segment
new_dos proc far
pushf ; save current flags,
sti ; and turn on ints
cmp ax, 0fedch ; our installation call?
jnz @F
mov ax, 0cdefh ; yes. Return the inverse
popf
iret
@@: cmp ah, 0dch ; one of our calls to toggle?
jnz @F ; no
call set_flag ; yes. Do it, and pass it on.
@@:
cmp cs:[in_use], TRUE ; no recursion!
jz @F
cmp cs:[switch_off], TRUE ; turned off?
jz @F
;;;;;;;;;;;;;;;;;;;;;;;;
;; We care about any file access call. If not a function we care about,
;; simply call the original DOS
;;;;;;;;;;;;;;;;;;;;;;;;
cmp ah, 0fh
jz open
; cmp ah, 013h
; jz delete
cmp ah, 016h
jz create
cmp ah, 03ch
jz h_create
cmp ah, 03dh
jz h_open
cmp ah, 06ch
jz h_open
; cmp ah, 041h
; jz n_delete
cmp ah, 04bh
jnz not_execute ; kludge
jmp execute
not_execute:
cmp ah, 05ah
jz h_create
cmp ah, 05bh
jz h_create
@@:
popf
jmp dword ptr cs:[old_dos]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Process all the FCB calls
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
open:
delete:
create:
mov cs:[last_dos], ax ; save for re-execute later
mov cs:[in_use], TRUE ; no recursion!
call dword ptr cs:[old_dos] ; do the original call
pushf
jnc op_good ; if good continue
jmp op_failed ; else why bother?
op_good:
push dx ; translate FCB filenames into
push ds ; ASCIIZ filenames
call fcb_stuff
push ax
push bx
mov ax, 03d00h ; open with handle for the
int 21h ; ioctl -- our psp
mov cs:[dos_handle], ax
call lookup ; main function call
mov bx, cs:[dos_handle] ; close file in our psp
mov ah, 03eh
int 21h
pop bx
pop ax
pop ds
pop dx
cmp cs:[was_compressed], TRUE ; must we play?
jnz normal1 ; no, skip
; yes, continue
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; first, close then delete the file using users fcb, then rename the tmp
;; file to the name in the fcb, then re-do the users operation
;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov cs:[was_compressed], FALSE
push ax
mov ah, 010h
int 21h ; close the file
mov ah, 013h
int 21h ; delete it
;;;;;;;;;;;;;;;
;; use handle rename. Filename in tmp_buffer still. Decompressed in temp file
;;;;;;;;;;;;;;;
call rename
mov ax, cs:[last_dos] ; reissue the call
int 21h
pop ax
normal1:
jmp op_failed ; common return
;;;;;;;;;;;;;;;;;;;
;; Process all handle operations here....
;;;;;;;;;;;;;;;;;;;
h_create:
h_open:
n_delete:
mov cs:[last_dos], ax
mov cs:[in_use], TRUE
call dword ptr cs:[old_dos] ; issue the users call.
pushf
sti
jc op_failed ; if it failed, simply return
mov cs:[dos_handle], ax
call set_6c ; kludge on 4.x 6c call
call lookup ; main call, file is open
call reset_6c ; reset from 4.x call
cmp cs:[was_compressed], TRUE ; do anything unusual?
jnz normal2 ; no
;;;;;;;;;;;;;;;;;;
;; Rename the file
;;;;;;;;;;;;;;;;;;
push ax
call handle_stuff ; do the rename
mov ax, cs:[last_dos] ; issue original call again
int 21h
pop ax
normal2:
op_failed:
popf
mov cs:[in_use], FALSE
ret 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Reset the compressed flag, close the file, delete it, rename it, return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
handle_stuff proc near
mov cs:[was_compressed], FALSE
push bx
push dx
push ds
mov bx, cs:[dos_handle]
mov ah, 03eh
int 21h
push cs
pop ds
mov dx, offset cs:tmp_buffer
mov ah, 041h
int 21h
call rename
pop ds
pop dx
pop bx
ret
handle_stuff endp
;;;;;;;;;;;;;;;;;;;;;
;; handles a little differently. Obviously, the file must be decompressed
;; before the call...
;;;;;;;;;;;;;;;;;;;;;
execute:
mov cs:[in_use], TRUE
push ax
push bx
mov ax, 03d00h ; open with handle
int 21h
mov cs:[dos_handle], ax
call lookup ; main work
mov bx, cs:[dos_handle] ; close file
mov ah, 03eh
int 21h
pop bx
pop ax
cmp cs:[was_compressed], TRUE ; skip exciting stuff?
jnz normal3 ; yes
push ax
call handle_stuff ; do the rename
pop ax
normal3:
popf
mov cs:[in_use], FALSE
jmp dword ptr cs:[old_dos] ; execute the program
new_dos endp
;;;;;;;;;;;;;;;;;;;;;;;;;
;; This routine makes an FCB entry, extended or not, into a semi-qualified
;; ASCIIZ filename
;;;;;;;;;;;;;;;;;;;;;;;;;
fcb_stuff proc near
push ax
push cx
push si
push di
push es
mov si, dx
cmp byte ptr ds:[si], 0ffh ; extended FCB?
jnz @F ; normal
add si, 7 ; point to the filename, 7
; bytes into extended FCB
@@: push cs
pop es
mov di, offset cs:tmp_buf2 ; ASCIIZ buffer
cmp byte ptr ds:[si], 0 ; default drive?
jz @F ; yes
mov al, ds:[si] ; no. add drive to buffer
mov es:[di], al
add byte ptr es:[di], 'A' - 1
mov byte ptr es:[di + 1], ':'
add di, 2 ; to filename position
@@: inc si ; and bypass the drive in FCB
mov cx, 11 ; filename(8) + extension(3)
; always left justified, space
; filled
file_lp:
mov al, ds:[si]
cmp al, ' ' ; space?
jz @F ; yes, so skip it
mov es:[di], al ; no, stuff the character
inc di
@@: cmp cx, 4 ; at start of extension?
jnz @F
mov byte ptr es:[di], '.' ; yes. stuff a dot
inc di
@@: inc si
loop file_lp ; for entire filename
mov byte ptr es:[di], 0 ; zero the end byte
pop es
pop di
pop si
pop cx
pop ax
push cs
pop ds
mov dx, offset tmp_buf2 ; point to the ASCIIZ filename
ret
fcb_stuff endp
;;;;;;;;;;;;;;;;;;;;;;;
;; Main routine.
;;
;; 1. Determine if a file or device. If device, return.
;; 2. Determine if fixed disk. If not, return
;; 3. Fully qualify file/pathname with undocumented AH=60h call
;; 4. Save users PSP, reset with our own. Handle table in users might be
;; full...
;; 5. Scan path name, isolate last '\' (separates path from filename)
;; saving the length of the path.
;; 6. Save the path in one location, the filename in another
;; 7. Stuff the name of the index file to the tail of the path
;; 8. Determine if this is the same index filepath as already loaded
;; 9. If not, write file if needed.
;;10. Load new file. If not there, create one -- and dummy the file out.
;;11. Reset to the beginning of the binary tree index
;;12. Find the file in index. If found, then goto 14
;;13. File not in index. If room, add it and return: file has not been
;; compressed, obviously. New entry updated with current date and time.
;;14. If file not compressed, merely update date and time and return.
;;15. If file is compressed, check to make sure it is, then decompress it
;; into temporary file named PCOMPRESS.$$$ and return
;;16. If, upon examination file is not a compressed one, reset to normal
;; status
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lookup:
mov cs:[was_compressed], FALSE
cld
push ax
push bx
push cx
push dx
push si
push di
push ds
push es
push dx
mov ax, 04400h ; ioctl call returns status
mov bx, cs:[dos_handle] ; in the dx register
mov cx, 2
xor dx, dx
int 21h
test dx, 080h ; devices have high bit set
pop dx
jz @F ; not set, a file
jmp done2 ; is a character device
@@: push cs ; a regular file
pop es
mov si, dx
mov di, offset tmp_buffer
mov ah, 60h ; undocumented DOS call to
int 21h ; fully qualify a file/path
; knows about SUBST and ASSIGN
mov dl, es:[di] ; get the disk letter
sub dl, 'A' - 1 ; A=1, B=2, etc...
mov ah, 1ch ; get disk parameters
int 21h
cmp byte ptr [bx], 0f8h ; on return, DS:BX points to
jz @F ; first FAT entry. F8 is fixed
jmp done2 ; disk. Anything else, we skip
@@: mov ah, 051h ; get users PSP, save
int 21h ; then swap our's in.
mov cs:[users_psp], bx
mov bx, cs:[my_psp]
mov ah, 050h
int 21h
;;;;;;;;;;;;;;;;;
;; separate the path from the filename into two buffers.
;;;;;;;;;;;;;;;;;
xor cx, cx
end_path:
mov al, es:[di]
cmp al, 0 ; end of path? (ASCIIZ)
jz @F ; yes
inc cx
inc di
cmp al, '\' ; path delimiter?
jnz end_path
mov cs:[tmp_len], cx ; save length
jmp end_path
@@: push cs ; create path to filename
pop ds
mov si, offset cs:[tmp_buffer]
mov di, offset cs:[dir_name]
mov cx, cs:[tmp_len]
cld
@@: movsb ; move the path over
loop @B
;;;;;;;;;;;;;;;;
;; Stuff index name onto path
;;;;;;;;;;;;;;;;
mov si, offset cs:[index_name]
mov cx, INDEX_NAME_LEN
cld
@@: movsb
loop @B
;;;;;;;;;;;;;;;;
;; Stuff temporary filename onto path
;;;;;;;;;;;;;;;;
mov si, offset cs:[tmp_buffer]
mov di, offset cs:[dir_name3]
mov cx, cs:[tmp_len]
cld
@@: movsb ; move the path over
loop @B
mov si, offset cs:[file_out]
mov cx, FILE_OUT_LEN
cld
@@: movsb
loop @B
;;;;;;;;;;;;;;;;;;;;;;;;
;; Same index name as last time?
;;;;;;;;;;;;;;;;;;;;;;;;
mov si, offset cs:[dir_name]
mov di, offset cs:[dir_name2]
mov cx, cs:[tmp_len]
cld
repz cmpsb ; pop out on no match
cmp byte ptr cs:[di], 'I' ;first letter of "INDEX.CMP"
jnz @F ; not a match
cmp cx, 0 ; all through and a match?
jnz @F
jmp a_match ; yes
; no match, fall through
;;;;;;;;;;;;;;;;;;;;;
;; New index file. Close the old one first, then create a new one
;; if needed with blank entries.
;;;;;;;;;;;;;;;;;;;;;
index_open:
@@: mov bx, [index_handle] ; close file on change
cmp bx, 0 ; first time?
jz @F ; yes
call update_file ; update the old file and
mov ah, 03eh ; do the close
int 21h
reset_attrb:
push cx
mov ax, 04301h
mov dx, offset dir_name2
mov cx, [old_attrb]
int 21h
pop cx
@@: mov ax, 04300h ; save the current attributes
mov dx, offset dir_name
int 21h
jc no_file
mov [old_attrb], cx
mov ax, 04301h ; make it readable
mov dx, offset dir_name
mov cx, 0
int 21h
mov ax, 3d02h ; open the new file up
mov dx, offset dir_name
int 21h
jc no_file ; no file. Create one.
mov cs:[index_handle], ax
mov bx, ax
mov cs:[num_files], 0 ;; ??
mov cx, size num_files + BUF_SIZE + MAX_FN_SIZE
mov dx, offset cs:[num_files] ; buffer to read into
mov ah, 03fh
int 21h
jnc a_match ; read okay!
no_file:
mov ah, 03ch ; create a new file
mov dx, offset cs:dir_name
mov cx, 0
int 21h
push cs:[attributes] ; mov attributes as if old file
pop cs:[old_attrb]
jnc @F
jmp done ;problem
;;;;;;;;;;;;;
;; Zero out what will be the contents of the new file
;;;;;;;;;;;;;
@@: mov bx, ax
mov cs:[index_handle], ax
mov cx, size num_files + BUF_SIZE + MAX_FN_SIZE
; Zero out buffer
mov di, offset cs:num_files
@@: mov byte ptr cs:[di], 0
inc di
loop @B
mov si, offset cs:[dir_name] ; but load index file name
mov di, offset cs:[dir_name2]
mov cx, cs:[tmp_len]
add cx, INDEX_NAME_LEN
cld
@@: movsb
loop @B
;;;;;;;;;;;;;;;;;;;
;; write out with zero files, index filename, an empty buffer
;;;;;;;;;;;;;;;;;;;
mov cx, BUF_SIZE + size num_files + MAX_FN_SIZE
mov dx, offset cs:num_files
mov ah, 040h
int 21h
call commit ; make sure it gets written
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; At this point, the index is loaded into memory. Find the file
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
a_match:
mov si, offset cs:[tmp_buffer]
add si, cs:[tmp_len] ;pointer to filename
mov di, offset cs:[file_name]
mov cs:[file_seg], es
mov cs:[file_off], di
@@: mov al, ds:[si] ; move filename again
mov es:[di], al
inc di
inc si
cmp al, 0
jnz @B
push cs
pop ds
mov ax, 1 ; get first one
call get_pointer ; bx gets pointer to entry
look_lp:
mov ds, cs:[file_seg] ; source seg
push cs
pop es ; target seg - the buffer
mov si, cs:[file_off] ; get back the filename
mov di, bx
add di, filename ; and point to it in record
mov cx, word ptr cs:[bx].filename_len
cmp cx, 0
jz no_match ; if empty record
@@: mov al, ds:[si]
cmp al, es:[di]
jnz no_match ; try again
inc si
inc di
loop @B
inc cs:[dirty_bit] ; found it!
call update ; a match. Update it.
jmp done
done:
mov bx, cs:[users_psp] ;reset users PSP and return
mov ah, 050h
int 21h
done2:
pop es
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
;;;;;;;;;;;;;;;
;; Current entry isn't what we want. Try next one.
;;;;;;;;;;;;;;;
no_match:
push cs
pop ds
mov ax, [bx].left_ptr ; assume less than
mov dx, left_ptr
jl @F
mov ax, [bx].right_ptr ; greater than, use right pointer
mov dx, right_ptr
@@: cmp ax, 0 ; end of the line?
jz @F ; yes
call get_pointer ; bx points to next entry
jmp look_lp ; se if a match
;;;;;;;;;;
;; insert routine
;;;;;;;;;;
@@: cmp cs:[num_files], MAX_FILES ; full already?
jnz @F ; no
jmp done ; yes. Ignore it.
@@: inc cs:[num_files] ; increase the count
mov ax, cs:[num_files]
add bx, dx ; left or right pointer offset
mov cs:[bx], ax
inc cs:[dirty_bit]
call get_pointer ; point to empty record
;;;;;;;;;;;;;;;;;;
;; copy the name into the buffer
;;;;;;;;;;;;;;;;;;
mov ds, cs:[file_seg] ; source seg
push cs
pop es ; target seg - the buffer
mov si, cs:[file_off] ; get back the filename
mov di, bx
add di, filename
xor cx, cx
cld
@@: movsb
inc cx
cmp byte ptr ds:[si], 0
jnz @B
;;;;;;;;;;;;;;;;
;; set up the rest of the entry
;;;;;;;;;;;;;;;;
mov byte ptr es:[di], 0 ; trailing null: nice for 'C'
mov cs:[bx].filename_len, cx
mov cs:[bx].left_ptr, 0
mov cs:[bx].right_ptr, 0
mov cs:[bx].access_cnt, 1
mov cs:[bx].status, NORMAL
inc cs:[dirty_bit]
call update ; add date and time
jmp done
;;;;;;;;;;;;;;;;;;;;;
;; Returns bx as in bx = (ax * size entry) + buffer offset
;; ax is the entry we seek
;;;;;;;;;;;;;;;;;;;;
get_pointer proc near
push cx
push dx
dec ax
mov bx, ax
mov cx, size file
mul cx
xchg ax, bx
add bx, offset cs:[buffer]
pop dx
pop cx
ret
get_pointer endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Write all the stuff in the current buffer out to disk
;;;;;;;;;;;;;;;;;;;;;;;;;;;
update_file proc near
push ax
push bx
push cx
push dx
push ds
push cs
pop ds
mov cs:[dirty_bit], 0 ; now we're clean
xor cx, cx ; lseek to beginning
xor dx, dx
mov ax, 04200h
mov bx, cs:[index_handle]
int 21h
mov dx, offset num_files ; write it
mov cx, BUF_SIZE + size num_files + MAX_FN_SIZE
mov ah, 040h
int 21h
call commit ; make sure it's written
pop ds
pop dx
pop cx
pop bx
pop ax
ret
update_file endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Add date and time. If compressed, decompress.
;;;;;;;;;;;;;;;;;;;;;;;;;;;
update:
mov ah, 02ah ; get the date
int 21h
mov cs:[bx].date, cx
mov cs:[bx].date + 2, dx
mov ah, 02ch ; get the time
int 21h
mov cs:[bx].time, cx ; stuff them
mov cs:[bx].time + 2, dx
inc cs:[bx].access_cnt ; up the access count
cmp cs:[ignore_status], TRUE
jz do_it_anyway
cmp cs:[bx].status, COMPRESSED
jnz @F ; normal
do_it_anyway:
push bx ; save the entry pointer
call decompress ; expand the file
pop bx
mov cs:[bx].status, NORMAL ; reset the status
inc cs:[dirty_bit] ; we have to write now.
@@: ret
;;;;;;;;;;;;;;;;;;;;;;;;
;; Is it really compressed?
;;;;;;;;;;;;;;;;;;;;;;;;
decompress:
push ax
push bx
push cx
push dx
push di
push si
;;;;;;;;;;;;
;; Read the first few bytes, see if it matches our unique entry
;;;;;;;;;;;;
mov dx, offset cs:tmp_buffer
add dx, filename
mov ax, 3d00h
int 21h
jc bad_decom2 ; can't decompress if can't open!
mov cs:[in_handle], ax
mov bx, ax
mov cx, TAG_LEN ; read the first coupla bytes in
mov dx, offset cs:tag_buf
mov ah, 03fh
int 21h
jc bad_decom ; too short for one of ours!
cmp ax, TAG_LEN
jl bad_decom
mov si, offset cs:tag ; our tag line?
mov di, dx
mov cx, TAG_LEN
@@: mov al, byte ptr cs:[si]
cmp al, byte ptr cs:[di]
jnz bad_decom
loop @B ; a match?
call do_decomp ; yes! Decompress it
bad_decom:
mov bx, cs:[in_handle] ; close the file
mov ah, 03eh
int 21h
bad_decom2:
cmp cs:[bx].status, NORMAL ; if it was normal, don't set dirty bit
jz @F
mov cs:[dirty_bit], TRUE
@@: pop si
pop di
pop dx
pop cx
pop bx
pop ax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Open a duplicate handle on the file, then close the duplicate handle.
;; This causes brain damaged dos to commit the file, regardless of
;; version
;;;;;;;;;;;;;;;;;;;;;;;;;;
commit proc near
push ax
push bx
mov ah, 045h
mov bx, cs:[index_handle]
int 21h
mov bx, ax
mov ah, 03eh
int 21h
pop bx
pop ax
ret
commit endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; LZW compression routines
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;
;; Zero out the table.
;;;;;;;;;;;;;;;;
init_tab proc near
push ax
push cx
push si
push ds
xor ax, ax
mov cx, 256
mov si, offset cs:codes
push cs
pop ds
lp:
mov [si].cmp_code, 0
mov [si].suffix, al
inc al
add si, size table
loop lp
mov cs:[codes_used], 256
pop ds
pop si
pop cx
pop ax
ret
init_tab endp
;;;;;;;;;;;;;;;;;;;;;
;; Actual decompression routine
;;;;;;;;;;;;;;;;;;;;;
do_decomp proc near
call paint ; XT owners don't think we crashed...
call init_tab
mov cs:[which_code], 0 ; initialize a bunch of variables
mov cs:[hold], 0
mov cs:[s_ptr], offset cs:stack ; not a real stack
mov cs:[out_cnt], 0
mov cs:[buff_cnt], 0
mov cs:[buff_size], 0
mov cs:[tmp], 0
push cs
pop ds
mov dx, offset cs:dir_name3 ; create the temporary file
xor cx, cx
mov ah, 03ch
int 21h
mov cs:[out_handle], ax
call get_code ; read a code in
mov bl, al ; save it
call stuff ; stuff on the stack
call unstuff ; unstuff into the output file
mov cs:[old_code], ax ; save it
mov cs:[last_char], bl ; save it
decode:
call get_code ; loop. Get a code. expand it.
jc exit ; we're done on carry
cmp ax, RESET_TABLE ; table full in compressor?
jnz no_exit
call init_tab ; yes. Reset everything
call get_code ; and start over
mov bl, al
call stuff
call unstuff
mov cs:[old_code], ax
jmp decode ; now we're back to normal
exit:
call output ; exit. Force output of last code
mov bx, cs:[out_handle]
mov ah, 03eh
int 21h ; close decomp'ed temp file
mov cs:[was_compressed], TRUE
call paint ; Yo! XT owner! Wake up!
ret ; good-run exit point
no_exit:
mov cs:[incode], ax ; save the code
cmp ax, cs:[codes_used] ; already in table?
jl @F ; yes
;;;;;;;;;;;;;;;;;;;;;;;;;
;; Special class of codes for highly repetitive strings. The code can
;; be transmitted before it exists! So, we simply add a new code
;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax, cs:[old_code]
mov bl, cs:[last_char]
call stuff
@@: push ax ; save the code
mov cx, size table ; get the suffix
mul cx
mov si, ax
add si, offset cs:codes
pop ax
cmp ax, 256 ; "low" code?
jl @F ; yup. End loop
mov bl,cs:[si].suffix
call stuff ; enter onto stack
mov ax, cs:[si].cmp_code ; next code in code sequence
jmp @B ; loop
@@: mov bl, cs:[si].suffix ; do the last char
mov cs:[last_char], bl ; and save it
call stuff ; add to stack
call unstuff ; write out whole stack
cmp cs:[codes_used], MAX_CODE ; full table?
jz @F ; yup
;;;;;;;;;;;;;;;;;;;;
;; Add the code into the table
;;;;;;;;;;;;;;;;;;;;;
mov ax, cs:[codes_used]
mov cx, size table
mul cx
mov si, ax
add si, offset cs:codes
mov ax, cs:[old_code]
mov cs:[si].cmp_code, ax
mov al, cs:[last_char]
mov cs:[si].suffix, al
inc cs:[codes_used] ; up the number of codes used in table
@@: mov ax, cs:[incode] ; save original code
mov cs:[old_code], ax
jmp decode ; next!
do_decomp endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The codes are actually 12 bit entities: a byte and a half. So, take
;; turns reading two bytes, returning 12 bits, then one byte, returning
;; old half byte (a nibble) and new byte. That's a code. And it fits
;; within a register. How nifty!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_code:
cmp cs:[which_code], 0 ; even or odd code?
jnz odd_code
call get_byte ; get the first byte
jc get_code_ret ; eof
xor ax, ax
mov al, byte ptr cs:[tmp] ; get byte
mov cl, 4 ; and pop it down a nibble
shl ax, cl
call get_byte ; get next byte
jc get_code_ret ; eof
mov bx, ax ; turn it into a code and save
xor ax, ax ; left over nibble.
mov al, byte ptr cs:[tmp]
mov cs:[hold], ax
and cs:[hold], 0fh ; set the leftover flag.
mov cl, 4
shr ax, cl
or ax, bx
mov cs:[which_code], 1
clc
jmp get_code_ret
odd_code:
mov ax, cs:[hold] ; retrieve left over nibble
mov cl, 8 ; pop it up
shl ax, cl
call get_byte ; get next byte
jc get_code_ret ; eof? Shouldn't happen
or al, byte ptr cs:[tmp] ; add it in
mov cs:[which_code], 0 ; set for even read next time
get_code_ret:
ret ; code in ax
;;;;;;;;;;;;;;;;;;;;;;
;; characfter in bl gets stuffed onto stack
;;;;;;;;;;;;;;;;;;;;;;
stuff:
push si
mov si, cs:[s_ptr]
inc cs:[s_ptr]
mov byte ptr cs:[si], bl
pop si
ret
;;;;;;;;;;;;;;;;;;;;;;;;
;; Get characters off stack in reverse order. Add to buffer. Write buffer
;; if it gets too large
;;;;;;;;;;;;;;;;;;;;;;;;
unstuff:
push ax
push bx
push si
mov si, cs:[s_ptr]
unstuff_lp:
dec si
cmp si, offset cs:stack
jl unstuff_end
cmp cs:[out_cnt], INBUF_SIZE
jnz @F
call output
@@: mov al, byte ptr cs:[si]
mov bx, cs:[out_cnt]
inc cs:[out_cnt]
add bx, offset cs:out_buf
mov byte ptr cs:[bx], al
jmp unstuff_lp
unstuff_end:
mov cs:[s_ptr], offset cs:stack ; reset the stack
pop si
pop bx
pop ax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Write the data out to the output temporary file
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
output proc near
cmp cs:[out_cnt], 0
jz output_ret
push ax
push bx
push cx
push dx
push ds
mov bx, cs:[out_handle]
mov cx, cs:[out_cnt]
mov ah, 040h
push cs
pop ds
mov dx, offset cs:out_buf
int 21h
mov cs:[out_cnt], 0
pop ds
pop dx
pop cx
pop bx
pop ax
output_ret:
ret
output endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Read a buffer in. Return next byte in buffer, reading in buffers as
;; required.
;; Return with carry set when you reach EOF.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_byte proc near
push ax
push bx
push cx
push dx
mov ax, cs:[buff_cnt]
cmp ax, cs:[buff_size]
jnz buffer_ok
mov dx, offset cs:in_buffer
mov cx, INBUF_SIZE ; must be evenly divisible by 1.5
mov bx, cs:[in_handle]
mov ah, 03fh
int 21h
mov cs:[buff_size], ax
mov cs:[buff_cnt], 0
cmp ax, 0
jz eof
buffer_ok:
clc
mov bx, cs:[buff_cnt]
inc cs:[buff_cnt]
mov al, byte ptr cs:[bx + in_buffer]
mov cs:[tmp], al
jmp get_byte_ret
eof:
stc
get_byte_ret:
pop dx
pop cx
pop bx
pop ax
ret
get_byte endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Simply rename the temporary file into the real filename name
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
rename proc near
push ax
push dx
push di
push ds
push es
push cs
pop ds
push cs
pop es
mov di, offset cs:tmp_buffer
mov dx, offset cs:dir_name3
mov ah, 056h
int 21h
pop es
pop ds
pop di
pop dx
pop ax
ret
rename endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Paint message on screen by exchanging bytes. Two calls and everything
;; is back to normal
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
paint proc near
push ax
push cx
push si
push di
push es
mov es, cs:[screen_segment]
mov si, SCREEN_OFFSET
xor di, di
mov cx, SCREEN_LEN
scr_lp1:
mov ax, word ptr es:[si]
xchg al, cs:[screen_msg + di]
xchg ah, cs:[screen_attrb + di]
mov word ptr es:[si], ax
inc si
inc si
inc di
loop scr_lp1
pop es
pop di
pop si
pop cx
pop ax
ret
paint endp
;; end of resident code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; start of transient code
;;;;;;;;;;;;;;;;;;;;;;
;; See if we're already installed. If so, print a message and exit
;; Otherwise, save the old Dos interrupt vector, take it over for our
;; purposes, output a nice message, and save our own psp in the code
;; segment before exiting with a bunch of memory saved
install:
mov ax, 0fedch
int 21h
cmp ax, 0cdefh
jnz @F
jmp already_in
@@: mov ax, 03521h
int 21h
mov cs:[old_dos], bx
mov cs:[old_dos + 2], es
mov ax, 02521h
push cs
pop ds
mov dx, offset new_dos
int 21h
mov dx, offset good_inst_msg
mov ah, 9
int 21h
mov ah, 051h
int 21h
mov cs:[my_psp], bx
mov ax, 03100h
mov si, 80h ; point to arguments
mov cl, byte ptr [si]
xor ch, ch
cmp cx, 0
jz @F
@@: inc si
cmp byte ptr [si], '-'
jz got_minus
cmp byte ptr [si], '/'
jnz not_arg
got_minus:
cmp byte ptr [si + 1], 'i'
jz got_arg
cmp byte ptr [si + 1], 'I'
jnz not_ignore
got_arg:
mov cs:[ignore_status], TRUE
jmp not_arg
not_ignore: ; -A#
cmp byte ptr [si + 1], 'a'
jz got_arg2
cmp byte ptr [si + 1], 'A'
jnz not_arg
got_arg2:
mov al, byte ptr [si + 2]
sub al, '0'
jl not_arg
cmp al, 3
jg not_arg
;0= visible, writable
;1= hidden, writable
;2= visible, not writable
;3= hidden, not writable
test al, 1
jz not_hidden
or cs:[attributes], 2
not_hidden:
test al, 2
jz not_write_protect
or cs:[attributes], 1
not_write_protect:
not_arg:
loop @B
@@: mov dx, offset install
shr dx, 1
shr dx, 1
shr dx, 1
shr dx, 1
inc dx
int 21h
int 20h
already_in:
mov dx, offset bad_inst_msg
mov ah, 9
int 21h
mov ax, 04c01h
int 21h
bad_inst_msg db CR, LF, BELL, 'DCOMPRES already installed...'
db CR, LF, '$'
good_inst_msg db CR, LF, 'DCOMPRES Copyright (c) 1989 by Ziff Communications Co. Program by Ross M. Greenberg'
db CR, LF, LF, 'COMPRESS installed.', CR, LF, LF, '$'
@@ equ good_inst_msg
cseg ends
end start