1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
; aplib_8088_small.S - size-optimized aPLib decompressor for 8088 - 145 bytes
;
; 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 aPLib data
; inputs:
; * ds:si: compressed aPLib data
; * es:di: output buffer
; output:
; * ax: decompressed size
; ---------------------------------------------------------------------------
apl_decompress:
push di ; remember decompression offset
cld ; make string operations go forward
; === register map ===
; al: bit queue
; ah: unused, but value is trashed
; bx: follows_literal
; cx: scratch register for reading gamma2 codes and storing copy length
; dx: match offset (and rep-offset)
; si: input (compressed data) pointer
; di: output (decompressed data) pointer
; bp: offset of .get_bit
mov al,080H ; clear bit queue(al) and set high bit to move into carry
xor dx,dx ; invalidate rep offset
mov bp,.get_bit ; load offset of .get_bit, to be used with call bp
.literal:
movsb ; read and write literal byte
.next_command_after_literal:
mov bx,03H ; set follows_literal(bx) to 3
.next_command:
call bp ; read 'literal or match' bit
jnc .literal ; if 0: literal
; 1x: match
call bp ; read '8+n bits or other type' bit
jc .other ; 11x: other type of match
; 10: 8+n bits match
call .get_gamma2 ; read gamma2-coded high offset bits
sub cx,bx ; high offset bits == 2 when follows_literal == 3 ?
; (a gamma2 value is always >= 2, so substracting follows_literal when it
; is == 2 will never result in a negative value)
jae .not_repmatch ; if not, not a rep-match
call .get_gamma2 ; read match length
jmp short .got_len ; go copy
.not_repmatch:
mov dh,cl ; transfer high offset bits to dh
mov dl,[si] ; read low offset byte in dl
inc si
call .get_gamma2 ; read match length
cmp dh,07DH ; offset >= 32000 ?
jae .increase_len_by2 ; if so, increase match len by 2
cmp dh,05H ; offset >= 1280 ?
jae .increase_len_by1 ; if so, increase match len by 1
cmp dx,0080H ; offset < 128 ?
jae .got_len ; if so, increase match len by 2, otherwise it would be a 7+1 copy
.increase_len_by2:
inc cx ; increase length
.increase_len_by1:
inc cx ; increase length
; copy cx bytes from match offset dx
.got_len:
push ds ; save ds:si (current pointer to compressed data)
push si
push es
pop ds
mov si,di ; point to destination in es:di - offset in dx
sub si,dx
rep movsb ; copy matched bytes
pop si ; restore ds:si
pop ds
mov bl,02H ; set follows_literal to 2 (bx is unmodified by match commands)
jmp short .next_command
; read gamma2-coded value into cx
.get_gamma2:
xor cx,cx ; initialize to 1 so that value will start at 2
inc cx ; when shifted left in the adc below
.gamma2_loop:
call .get_dibits ; read data bit, shift into cx, read continuation bit
jc .gamma2_loop ; loop until a zero continuation bit is read
ret
; handle 7 bits offset + 1 bit len or 4 bits offset / 1 byte copy
.other:
xor cx,cx
call bp ; read '7+1 match or short literal' bit
jc .short_literal ; 111: 4 bit offset for 1-byte copy
; 110: 7 bits offset + 1 bit length
mov dl,[si] ; read offset + length in dl
inc si
inc cx ; prepare cx for length below
shr dl,1 ; shift len bit into carry, and offset in place
je .done ; if zero offset: EOD
adc cx,cx ; len in cx: 1*2 + carry bit = 2 or 3
xor dh,dh ; clear high bits of offset
jmp short .got_len
; 4 bits offset / 1 byte copy
.short_literal:
call .get_dibits ; read 2 offset bits
adc cx,cx
call .get_dibits ; read 2 offset bits
adc cx,cx
xchg ax,cx ; preserve bit queue in cx, put offset in ax
jz .write_zero ; if offset is 0, write a zero byte
; short offset 1-15
mov bx,di ; point to destination in es:di - offset in ax
sub bx,ax ; we trash bx, it will be reset to 3 when we loop
mov al,[es:bx] ; read byte from short offset
.write_zero:
stosb ; copy matched byte
xchg ax,cx ; restore bit queue in al
jmp .next_command_after_literal
.done:
pop ax ; retrieve the original decompression offset
xchg di,ax ; compute decompressed size
sub ax,di
ret
.get_dibits:
call bp ; read data bit
adc cx,cx ; shift into cx
.get_bit:
add al,al ; shift bit queue, and high bit into carry
jnz .got_bit ; queue not empty, bits remain
lodsb ; read 8 new bits
adc al,al ; shift bit queue, and high bit into carry
.got_bit:
ret
|