kolibrios/programs/mhc/trunk/mhc.asm

978 lines
13 KiB
NASM
Raw Normal View History

;
; MHC archiver for MenuetOS - very fast compression tool
;
; version 0.09
;
; Written by Nikita Lesnikov (nlo_one@mail.ru, Republic of Belarus, Sluck)
;
;==============================================================================
;
; Brief file format description:
;
; +-----------+------------------------+
; File structure: | Method ID | Compressed data |
; +-----------+------------------------+
;
; Methods list:
;
; 0. LZP (order-2 specified specially for *.ASM,*.RAW and MeOS executables)
;
; New methods can be easily added without loss of compatibility
; with older versions
;
;==============================================================================
; SYSTEM HEADER
use32
org 0x0
db "MENUET01"
dd 0x01
dd ENTRANCE
dd MHC_END
dd 0x300000 ; 3 megs of memory needed
dd 0x2FF000
dd 0x0
dd 0x0
include 'lang.inc'
include 'macros.inc'
; CODE AREA
ENTRANCE:
; ======== user interface =========
call draw_window ; draw the window
still:
mov eax,10 ; wait for event
int 0x40
cmp eax,1 ; redraw?
jnz no_redraw
call draw_window
no_redraw:
cmp eax,2 ; key pressed?
jz key
cmp eax,3 ; button pressed?
jz button
jmp still
; Key handler
key:
mov eax,2 ; read it
int 0x40
shr eax,8
cmp byte [editstate],0
jz still
cmp al,8 ; backspace
jnz no_bksp
cmp byte [editpos],0
jz no_del_last
dec byte [editpos]
xor ebx,ebx
mov bl,byte [editpos]
add ebx,cmfile
cmp byte [editstate],2
jnz no_add_base_1
add ebx,12
no_add_base_1:
mov byte [ebx],32
no_del_last:
call draw_info
jmp still
no_bksp:
cmp al,13 ; enter
jnz no_enter
mov byte [editstate],0
call draw_info
jmp still
no_enter:
cmp eax,dword 31
jbe no_lit
cmp eax,dword 95
jb capital
sub eax,32
capital:
xor ebx,ebx
mov bl,byte [editpos]
add ebx,cmfile
cmp byte [editstate],2
jnz no_add_base_2
add ebx,12
no_add_base_2:
mov byte [ebx],al
inc byte [editpos]
cmp byte [editpos],12
jnz no_null_state
mov byte [editstate],0
no_null_state:
call draw_info
no_lit:
jmp still
; Button handler
button:
mov eax,17
int 0x40
cmp ah,1
jnz no_quit
mov eax,-1
int 0x40
no_quit:
cmp ah,4
jnz nofirst
cld
mov byte [editstate],1
mov edi,cmfile
mov eax,0x20202020
mov ecx,3
rep stosd
mov byte [editpos],0
mov byte [msgid],0
call draw_info
nofirst:
cmp ah,5
jnz nosecond
cld
mov byte [editstate],2
mov edi,iofile
mov eax,0x20202020
mov ecx,3
rep stosd
mov byte [editpos],0
mov byte [msgid],0
call draw_info
nosecond:
cmp ah,2
jnz no_compress
call compress
no_compress:
cmp ah,3
jnz no_decompress
call decompress
no_decompress:
cmp ah,6
jnz no_delete_io
pusha
mov eax,32
mov ebx,iofile
int 0x40
popa
no_delete_io:
cmp ah,7
jnz no_delete_archive
pusha
mov eax,32
mov ebx,cmfile
int 0x40
popa
no_delete_archive:
jmp still
; WINDOW DRAW
draw_window:
mov eax,12 ; Start redrawing
mov ebx,1
int 0x40
xor eax,eax ; Define window
mov ebx,100*65536+240
mov ecx,100*65536+130
mov edx,0x02AAAAAA
mov esi,0x80777777
mov edi,0x00777777
int 0x40
mov eax,4 ; Draw all needed texts
mov ebx,8*65536+8
mov ecx,0x00FFFFFF
mov edx,title
mov esi,arclab-title
int 0x40
xor ecx,ecx
mov edx,arclab
mov esi,unplab-arclab
add ebx,10*65536+28
int 0x40
mov edx,unplab
mov esi,fin_text-unplab
add ebx,18
int 0x40
pusha
mov eax,8 ; Buttons
mov ebx,222*65536+10
mov ecx,6*65536+10
mov edx,1
mov esi,0x555555
int 0x40
mov ebx,15*65536+100
mov ecx,70*65536+13
inc edx
int 0x40
inc edx
add ebx,110*65536
int 0x40
inc edx
mov ebx,214*65536+11
mov ecx,33*65536+11
int 0x40
inc edx
add ecx,18*65536
int 0x40
inc edx
mov ebx,15*65536+100
mov ecx,86*65536+13
int 0x40
inc edx
add ebx,110*65536
int 0x40
popa
mov ecx,0x00FFFFFF
mov edx,keylab
mov esi,dellab-keylab
add ebx,19
int 0x40
mov edx,dellab
mov esi,title-dellab
add ebx,16
int 0x40
call draw_info
mov eax,12 ; Finish redrawing
mov ebx,2
int 0x40
ret
draw_info: ; Draw filenames and compressor state
activecolor equ 0x00112299
pusha ; Save registers
mov eax,13 ; Clean draw area
mov ebx,127*65536+85
mov ecx,33*65536+33
mov edx,0x00AAAAAA
int 0x40
mov eax,4 ; Draw filenames
mov ebx,134*65536+36
mov edx,cmfile
xor ecx,ecx
mov esi,12
cmp byte [editstate],1
jnz no_active_1
mov ecx,activecolor
no_active_1:
int 0x40
xor ecx,ecx
cmp byte [editstate],2
jnz no_active_2
mov ecx,activecolor
no_active_2:
add ebx,18
add edx,12
int 0x40
mov eax,13 ; Clean info area
mov ebx,14*65536+210
mov ecx,107*65536+14
mov edx,0x00AAAAAA
int 0x40
cmp byte [msgid],0 ; Draw info string
jz notype
mov ebx,16*65536+110
xor ecx,ecx
mov esi,16
mov al, byte [msgid]
dec al
shl al,4
xor ah,ah
xor edx,edx
mov dx,ax
add edx,msgtable
mov eax,4
int 0x40
notype:
popa ; Restore registers
ret
; interface data
keylab db " COMPRESS DECOMPRESS"
dellab db " DELETE I/O DELETE *.MHC"
title db "MHC 0.09"
arclab db "COMPRESSED FILE:"
unplab db "INPUT/OUTPUT FILE:"
fin_text:
cmfile db "FILENAME.MHC"
iofile db "FILENAME.XYZ"
editstate db 0
editpos db 0
msgid db 0
msgtable:
db "COMPRESSING... "
db "DECOMPRESSING..."
db "I/O NOT FOUND! "
db "*.MHC NOT FOUND!"
db "INVALID METHOD! "
; ======== compression/decompression engine ========
; Adresses declaration
hashtable equ MHC_END
ifile equ hashtable+65536*4
ofile equ ifile+1000000
compress: ; File compression
call fill_filebufs
mov eax,6
mov ebx,iofile
xor ecx,ecx
mov edx,ecx
not edx
mov esi,ifile
int 0x40
cmp eax,0xFFFFFFFF
jnz compress_filefound ; i/o file not found
mov byte [msgid],3
call draw_info
ret
compress_filefound:
mov byte [msgid],1
call draw_info
jmp lzp_compress ; compress with order-2 LZP
compress_dumpdata:
push edx
mov eax,32
mov ebx,cmfile
int 0x40
mov eax,33
pop edx
mov ebx,cmfile
mov ecx,ofile
xor esi,esi
int 0x40
mov byte [msgid],0
call draw_info
ret
decompress: ; File decompression
call fill_filebufs
mov eax,6
mov ebx,cmfile
xor ecx,ecx
mov edx,ecx
not edx
mov esi,ofile
int 0x40
cmp eax,0xFFFFFFFF
jnz decompress_filefound ; *.mhc file not found
mov byte [msgid],4
call draw_info
ret
decompress_filefound:
cmp byte [ofile],0 ; Invalid method!
jz right_method
mov byte [msgid],5
call draw_info
ret
right_method:
mov byte [msgid],2
call draw_info
jmp lzp_decompress
decompress_dumpdata:
push edx
mov eax,32
mov ebx,iofile
int 0x40
mov eax,33
pop edx
mov ebx,iofile
mov ecx,ifile
xor esi,esi
int 0x40
mov byte [msgid],0
call draw_info
ret
fill_filebufs: ; Fill filebufs with garbage to simplify matching
pusha
cld
mov eax,0xF7D9A03F ; <- "magic number" :) just garbage...
mov ecx,2000000/4
mov edi,ifile
rep stosd
popa
ret
; ==== algorithms section ====
; Method 0: LZP compression algorithm
lzp_compress: ; EDX - how much bytes to dump
cld ; clear direction flag
mov esi,ifile ; init pointers
mov edi,ofile
push eax ; write header: ID0+4bfilesize => total 5 bytes
xor eax,eax
stosb
pop eax
stosd
pusha ; fill hash table
mov eax,ifile
mov edi,hashtable
mov ecx,65536
rep stosd
popa
add eax,esi ; calculate endpointer
mov dword [endpointer],eax
movsw ; copy three bytes
movsb
mov dword [controlp],edi
inc edi
mov byte [controld],0
mov byte [controlb],0
c_loop:
cmp dword [endpointer],esi ; check end of file
ja c_loop_ok
jmp finish_c_loop
c_loop_ok:
call chash
call compare
jz two_match_c
lodsb
mov byte [literal],al
call chash
call compare
jz lit_match_c
mov al,0
call putbit
mov al,byte [literal]
stosb
movsb
jmp end_c_loop
lit_match_c:
mov al,1
call putbit
mov al,0
call putbit
mov al,byte [literal]
stosb
jmp encode_match
two_match_c:
mov al,1
call putbit
call putbit
encode_match:
call incpos
call compare
jz one_c
mov al,0
call putbit
jmp end_c_loop
one_c:
call incpos
mov al,1
call putbit
call compare
jnz ec1
call incpos
call compare
jnz ec2
call incpos
call compare
jnz ec3
call incpos
mov al,1
call putbit
call putbit
call compare
jnz ec4
call incpos
call compare
jnz ec5
call incpos
call compare
jnz ec6
call incpos
call compare
jnz ec7
call incpos
call compare
jnz ec8
call incpos
call compare
jnz ec9
call incpos
call compare
jnz ec10
call incpos
mov al,1
call putbit
call putbit
call putbit
xor ecx,ecx
match_loop_c:
cmp esi,dword [endpointer]
jae out_match_loop_c
call compare
jnz out_match_loop_c
inc ecx
call incpos
jmp match_loop_c
out_match_loop_c:
mov al,0xFF
out_lg:
cmp ecx,255
jb out_lg_out
stosb
sub ecx,255
jmp out_lg
out_lg_out:
mov al,cl
stosb
jmp end_c_loop
ec10:
mov al,1
call putbit
call putbit
mov al,0
call putbit
jmp end_c_loop
ec9:
mov al,1
call putbit
mov al,0
call putbit
mov al,1
call putbit
jmp end_c_loop
ec8:
mov al,1
call putbit
mov al,0
call putbit
call putbit
jmp end_c_loop
ec7:
mov al,0
call putbit
mov al,1
call putbit
call putbit
jmp end_c_loop
ec6:
mov al,0
call putbit
mov al,1
call putbit
mov al,0
call putbit
jmp end_c_loop
ec5:
mov al,0
call putbit
call putbit
mov al,1
call putbit
jmp end_c_loop
ec4:
mov al,0
call putbit
call putbit
call putbit
jmp end_c_loop
ec3:
mov al,1
call putbit
mov al,0
call putbit
jmp end_c_loop
ec2:
mov al,0
call putbit
mov al,1
call putbit
jmp end_c_loop
ec1:
mov al,0
call putbit
call putbit
end_c_loop:
jmp c_loop
finish_c_loop:
mov eax,dword [controlp] ; store last tagbyte
mov bl,byte [controld]
mov [eax], byte bl
sub edi,ofile ; calculate dump size
mov edx,edi
jmp compress_dumpdata
; LZP decompression algorithm
lzp_decompress: ; EDX - how much bytes to dump
cld
mov edi,ifile
mov esi,ofile+1
pusha ; fill hash table
mov eax,ifile
mov edi,hashtable
mov ecx,65536
rep stosd
popa
lodsd
mov ebx,edi
add ebx,eax
mov dword [endpointer],ebx
movsw
movsb
lodsb
mov byte [controld],al
mov byte [controlb],0
d_loop:
cmp dword [endpointer],edi
ja d_loop_ok
jmp finish_d_loop
d_loop_ok:
call getbit
cmp al,0
jnz match_d
call dhash
movsb
call dhash
movsb
jmp end_d_loop
match_d:
call getbit
cmp al,0
jnz no_literal_before_match
call dhash
movsb
no_literal_before_match:
call dhash
mov ecx,1
call copymatch
call getbit
cmp al,0
jz end_d_loop
mov ecx,1
call copymatch
call getbit
cmp al,0
jz dc2
mov ecx,2
call copymatch
call getbit
cmp al,0
jz end_d_loop
mov ecx,1
call copymatch
call getbit
cmp al,0
jz dc4
mov ecx,4
call copymatch
call getbit
cmp al,0
jz dc5
call getbit
cmp al,0
jz dc6
mov ecx,3
call copymatch
do:
lodsb
xor ecx,ecx
mov cl,al
call copymatch
cmp al,0xFF
jnz end_do
jmp do
end_do:
jmp end_d_loop
dc6:
mov ecx,2
call copymatch
jmp end_d_loop
dc5:
call getbit
cmp al,0
jz ndc5
mov ecx,1
call copymatch
ndc5:
jmp end_d_loop
dc4:
call getbit
cmp al,0
jz ndc4
call getbit
mov ecx,3
cmp al,1
jz ndcc4
dec ecx
ndcc4:
call copymatch
jmp end_d_loop
ndc4:
call getbit
cmp al,0
jz ndccc4
mov ecx,1
call copymatch
ndccc4:
jmp end_d_loop
dc2:
call getbit
cmp al,0
jz ndc2
mov ecx,1
call copymatch
ndc2:
end_d_loop:
jmp d_loop
finish_d_loop:
mov edx, dword [ofile+1]
jmp decompress_dumpdata
; LZP subroutines
putbit: ; bit -> byte tag, AL holds bit for output
pusha
mov cl,byte [controlb]
shl al,cl
mov bl,byte [controld]
or bl,al
mov byte [controld],bl
inc cl
cmp cl,8
jnz just_increment
mov byte [controlb],0
mov byte [controld],0
push edi
mov edi, dword [controlp]
mov al,bl
stosb
pop edi
mov dword [controlp],edi
popa
inc edi
ret
just_increment:
mov byte [controlb],cl
popa
ret
getbit: ; tag byte -> bit, AL holds input
push ecx
mov al,byte [controld]
mov cl,byte [controlb]
shr al,cl
and al,1
inc cl
cmp cl,8
jnz just_increment_d
mov byte [controlb],0
push eax
lodsb
mov byte [controld],al
pop eax
pop ecx
ret
just_increment_d:
mov byte [controlb],cl
pop ecx
ret
chash: ; calculate hash -> mp -> fill position
pusha
xor eax,eax
mov al, byte [esi-1]
mov ah, byte [esi-2]
shl eax,2
add eax,hashtable
mov edx,dword [eax]
mov dword [mp],edx
mov dword [eax],esi
popa
ret
dhash: ; calculate hash -> mp -> fill position
pusha
xor eax,eax
mov al, byte [edi-1]
mov ah, byte [edi-2]
shl eax,2
add eax,hashtable
mov edx,dword [eax]
mov dword [mp],edx
mov dword [eax],edi
popa
ret
copymatch: ; ECX bytes from [mp] to [rp]
push esi
mov esi,dword [mp]
rep movsb
mov dword [mp],esi
pop esi
ret
compare: ; compare [mp] with [cpos]
push edi
push esi
mov edi,dword [mp]
cmpsb
pop esi
pop edi
ret
incpos:
inc dword [mp]
inc esi
ret
; LZP algorithm data
endpointer dd 0
controlp dd 0
controlb db 0
controld db 0
mp dd 0
literal db 0
MHC_END: ; the end... - Nikita Lesnikov (nlo_one)