mirror of
https://github.com/emmanuel-marty/lzsa.git
synced 2024-12-29 00:31:54 +00:00
177 lines
6.3 KiB
ArmAsm
Executable File
177 lines
6.3 KiB
ArmAsm
Executable File
; decompress_small.S - space-efficient decompressor implementation for 8088
|
|
;
|
|
; Copyright (C) 2019 Emmanuel Marty
|
|
;
|
|
; This software is provided 'as-is', without any express or implied
|
|
; warranty. In no event will the authors be held liable for any damages
|
|
; arising from the use of this software.
|
|
;
|
|
; Permission is granted to anyone to use this software for any purpose,
|
|
; including commercial applications, and to alter it and redistribute it
|
|
; freely, subject to the following restrictions:
|
|
;
|
|
; 1. The origin of this software must not be misrepresented; you must not
|
|
; claim that you wrote the original software. If you use this software
|
|
; in a product, an acknowledgment in the product documentation would be
|
|
; appreciated but is not required.
|
|
; 2. Altered source versions must be plainly marked as such, and must not be
|
|
; misrepresented as being the original software.
|
|
; 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
segment .text
|
|
bits 16
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Decompress raw LZSA2 block
|
|
; inputs:
|
|
; * ds:si: raw LZSA2 block
|
|
; * es:di: output buffer
|
|
; output:
|
|
; * ax: decompressed size
|
|
; ---------------------------------------------------------------------------
|
|
|
|
lzsa2_decompress:
|
|
push di ; remember decompression offset
|
|
cld ; make string operations (lods, movs, stos..) move forward
|
|
|
|
xor cx,cx
|
|
mov bx,0100H
|
|
xor bp,bp
|
|
|
|
.decode_token:
|
|
mov ax,cx ; clear ah - cx is zero from above or from after rep movsb in .copy_match
|
|
lodsb ; read token byte: XYZ|LL|MMMM
|
|
mov dx,ax ; keep token in dl
|
|
|
|
and al,018H ; isolate literals length in token (LL)
|
|
mov cl,3
|
|
shr al,cl ; shift literals length into place
|
|
|
|
cmp al,03H ; LITERALS_RUN_LEN_V2?
|
|
jne .got_literals ; no, we have the full literals count from the token, go copy
|
|
|
|
call .get_nibble ; get extra literals length nibble
|
|
add al,cl ; add len from token to nibble
|
|
cmp al,012H ; LITERALS_RUN_LEN_V2 + 15 ?
|
|
jne .got_literals ; if not, we have the full literals count, go copy
|
|
|
|
lodsb ; grab extra length byte
|
|
add al,012H ; overflow?
|
|
jnc .got_literals ; if not, we have the full literals count, go copy
|
|
|
|
lodsw ; grab 16-bit extra length
|
|
|
|
.got_literals:
|
|
xchg cx,ax
|
|
rep movsb ; copy cx literals from ds:si to es:di
|
|
|
|
test dl,0C0h ; check match offset mode in token (X bit)
|
|
js .rep_match_or_large_offset
|
|
|
|
;;cmp dl,040H ; check if this is a 5 or 9-bit offset (Y bit)
|
|
; discovered via the test with bit 6 set
|
|
xchg cx,ax ; clear ah - cx is zero from the rep movsb above
|
|
jne .offset_9_bit
|
|
|
|
; 5 bit offset
|
|
cmp dl,020H ; test bit 5
|
|
call .get_nibble_x
|
|
jmp short .dec_offset_top
|
|
|
|
.offset_9_bit: ; 9 bit offset
|
|
lodsb ; get 8 bit offset from stream in A
|
|
dec ah ; set offset bits 15-8 to 1
|
|
test dl,020H ; test bit Z (offset bit 8)
|
|
je .get_match_length
|
|
.dec_offset_top:
|
|
dec ah ; clear bit 8 if Z bit is clear
|
|
; or set offset bits 15-8 to 1
|
|
jmp short .get_match_length
|
|
|
|
.rep_match_or_large_offset:
|
|
;;cmp dl,0c0H ; check if this is a 13-bit offset or a 16-bit offset/rep match (Y bit)
|
|
jpe .rep_match_or_16_bit
|
|
|
|
; 13 bit offset
|
|
|
|
cmp dl,0A0H ; test bit 5 (knowing that bit 7 is also set)
|
|
xchg ah,al
|
|
call .get_nibble_x
|
|
sub al,2 ; substract 512
|
|
jmp short .get_match_length_1
|
|
|
|
.rep_match_or_16_bit:
|
|
test dl,020H ; test bit Z (offset bit 8)
|
|
jne .repeat_match ; rep-match
|
|
|
|
; 16 bit offset
|
|
lodsb ; Get 2-byte match offset
|
|
|
|
.get_match_length_1:
|
|
xchg ah,al
|
|
lodsb ; load match offset bits 0-7
|
|
|
|
.get_match_length:
|
|
xchg bp,ax ; bp: offset
|
|
.repeat_match:
|
|
xchg ax,dx ; ax: original token
|
|
and al,07H ; isolate match length in token (MMM)
|
|
add al,2 ; add MIN_MATCH_SIZE_V2
|
|
|
|
cmp al,09H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2?
|
|
jne .got_matchlen ; no, we have the full match length from the token, go copy
|
|
|
|
call .get_nibble ; get extra literals length nibble
|
|
add al,cl ; add len from token to nibble
|
|
cmp al,018H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15?
|
|
jne .got_matchlen ; no, we have the full match length from the token, go copy
|
|
|
|
lodsb ; grab extra length byte
|
|
add al,018H ; overflow?
|
|
jnc .got_matchlen ; if not, we have the entire length
|
|
je short .done_decompressing ; detect EOD code
|
|
|
|
lodsw ; grab 16-bit length
|
|
|
|
.got_matchlen:
|
|
xchg cx,ax ; copy match length into cx
|
|
push ds ; save ds:si (current pointer to compressed data)
|
|
xchg si,ax
|
|
push es
|
|
pop ds
|
|
lea si,[bp+di] ; ds:si now points at back reference in output data
|
|
rep movsb ; copy match
|
|
xchg si,ax ; restore ds:si
|
|
pop ds
|
|
jmp .decode_token ; go decode another token
|
|
|
|
.done_decompressing:
|
|
pop ax ; retrieve the original decompression offset
|
|
xchg di,ax ; compute decompressed size
|
|
sub ax,di
|
|
ret ; done
|
|
|
|
.get_nibble_x:
|
|
cmc ; carry set if bit 4 was set
|
|
rcr al,1
|
|
call .get_nibble ; get nibble for offset bits 0-3
|
|
or al,cl ; merge nibble
|
|
rol al,1
|
|
xor al,0E1H ; set offset bits 7-5 to 1
|
|
ret
|
|
|
|
.get_nibble:
|
|
neg bh ; nibble ready?
|
|
jns .has_nibble
|
|
|
|
xchg bx,ax
|
|
lodsb ; load two nibbles
|
|
xchg bx,ax
|
|
|
|
.has_nibble:
|
|
mov cl,4 ; swap 4 high and low bits of nibble
|
|
ror bl,cl
|
|
mov cl,0FH
|
|
and cl,bl
|
|
ret
|