516 lines
16 KiB
PHP
516 lines
16 KiB
PHP
;** Decoding macros
|
|
;
|
|
; These walk a state machine based on where a command (i.e., char or string)
|
|
; begins.
|
|
|
|
|
|
;** BitsAt - Extract from bit position n some bits
|
|
;
|
|
; Macro parameter:
|
|
; n bit position to begin extract
|
|
; cbits number of bits to extract
|
|
; Entry: eax working data
|
|
; esi input stream
|
|
; edi output stream
|
|
; Exit: eax updated so that next data begins in al
|
|
; esi/edi updated
|
|
; ecx contains data
|
|
; Uses: none
|
|
|
|
BitsAt macro n,cbits
|
|
.errnz n eq 0
|
|
if (n+cbits) lt 8 ; entire operation occurs in low byte
|
|
CopyBits cx,ax,n,cbits ; (cx) = desired bits rightmost
|
|
elseif (n+cbits) lt 16 ; operation consumes byte
|
|
CopyBits cx,ax,n,cbits ; (cx) = desired bits rightmost
|
|
lodsb ; (ah/al) = next input
|
|
xchg al,ah ; (al/ah) = next input
|
|
elseif (n+cbits) eq 16 ; operation consumes remainder of buffered data
|
|
CopyBits cx,ax,n,cbits ; (cx) = desired bits rightmost
|
|
lodsw ; (al/ah) = next input
|
|
else ; operation consumes into unbuffered data
|
|
mov ecx,eax
|
|
lodsw
|
|
shrd cx,ax,n
|
|
and ecx,(1 shl cbits)-1
|
|
endif
|
|
endm
|
|
|
|
|
|
;** CmdAt - macro that processes a command at a bit position
|
|
;
|
|
; Macro parameter:
|
|
; n bit position where command is expected
|
|
; Entry: eax working data, command begins in al
|
|
; esi points to input stream
|
|
; edi points to output stream
|
|
; Exit: eax updated so that next command begins in al
|
|
; esi/edi updated
|
|
; EXPECTS FALL-THROUGH TO NEXT CmdAT
|
|
; Uses: ecx, edx (not directly, but by virtue of OffsetAt, which
|
|
; in turn calls LengthAt....)
|
|
|
|
CmdAt macro n
|
|
local ca1
|
|
|
|
align4
|
|
public CmdAt&n
|
|
CmdAt&n:
|
|
if n eq 7
|
|
ror eax,1
|
|
test al,11b shl 6
|
|
rol eax,1
|
|
else
|
|
test al,11b shl n
|
|
endif
|
|
jpo ca1
|
|
|
|
OffsetAt %(n+1)
|
|
align4 ; note that OffsetAt jumps away
|
|
ca1: ; so there is no fall-through penalty
|
|
CharAt %(n+1)
|
|
endm
|
|
|
|
|
|
;** CharAt - macro that processes a character at a bit position
|
|
;
|
|
; Macro parameter:
|
|
; n bit position where char is expected
|
|
; Entry: eax working data, char may be in ah
|
|
; esi input stream
|
|
; edi output stream
|
|
; Exit: eax updated so that next command begins in al
|
|
; esi/edi updated
|
|
; Uses: ch
|
|
|
|
CharAt macro n
|
|
if n eq 8
|
|
mov al,ah ; (al) = char for output
|
|
XlatChr
|
|
CheckOffset
|
|
stosb ; store it
|
|
lodsw ; (al/ah) = next input
|
|
else
|
|
if n eq 1
|
|
shr eax,1 ; (al) = byte for output
|
|
XlatChr
|
|
CheckOffset
|
|
stosb ; store it
|
|
add eax,eax ; (ah) = next byte
|
|
lodsb ; (ah/al) = next input
|
|
else
|
|
mov ch,ah ; (ch) = saved next input
|
|
shr eax,n ; (al) = byte for output
|
|
XlatChr
|
|
CheckOffset
|
|
stosb ; store it
|
|
lodsb ; (al) = byte-after-next
|
|
mov ah,ch ; (ah/al) = next input
|
|
endif
|
|
xchg al,ah ; (al/ah) = next input
|
|
endif
|
|
endm
|
|
|
|
|
|
;** OffsetAt - Parse an offset at a bit position
|
|
;
|
|
; Macro parameter:
|
|
; n bit position where offset is expected
|
|
; Entry: cbits number of bits in offset
|
|
; eax working data, offset may begin in ah
|
|
; esi input stream
|
|
; edi output stream
|
|
; Exit: eax updated so that length begins in al
|
|
; ecx offset
|
|
; esi/edi updated
|
|
; Uses: ecx
|
|
|
|
OffsetAt macro n
|
|
local try8,try12
|
|
|
|
public OffsetAt&n
|
|
OffsetAt&n:
|
|
CheckBit a,n ; does a 6-bit offset follow?
|
|
jnz try8 ; no, try an 8-bit offset
|
|
BitsAt %(n+1),6 ; yes, load it into (ecx) and go
|
|
Jump LengthAt,%((n+7) mod 8)
|
|
align4
|
|
try8:
|
|
CheckBit a,%(n+1) ; does an 8-bit offset follow?
|
|
jnz try12 ; no, must be a 12-bit offset
|
|
BitsAt %(n+2),8 ; yes, load it into (ecx)
|
|
add ecx,MAX_6BIT_OFFSET+1 ;
|
|
Jump LengthAt,%((n+10) mod 8); go process the following length
|
|
align4
|
|
try12:
|
|
BitsAt %(n+2),12 ; load 12-bit offset into (ecx)
|
|
add ecx,MAX_8BIT_OFFSET+1 ;
|
|
Jump LengthAt,%((n+14) mod 8); go process the following length
|
|
endm
|
|
|
|
|
|
;** LengthAt - parse off a length at a position and move the bytes
|
|
;
|
|
; LengthAt parses off a length (gamma-prime encoded), moves the
|
|
; relevant string, and dispatches to the next command.
|
|
;
|
|
; Macro parameter:
|
|
; n bit position to begin extract
|
|
; Entry: eax working data
|
|
; ecx offset for string
|
|
; esi input stream
|
|
; edi output stream
|
|
; Exit: eax updated so that next data begins in al
|
|
; esi/edi updated
|
|
; Uses: ecx, edx
|
|
|
|
LengthAt macro n
|
|
local try3,try5,try7,try9,tryGeneral,done,error
|
|
|
|
% ifidni <LastErrBJump>,<DecodeError>
|
|
LastErrBJump equ <error>
|
|
endif
|
|
|
|
align4
|
|
public LengthAt&n
|
|
LengthAt&n:
|
|
jecxz error ; check for 0 offset (illegal)
|
|
cmp ecx,SPECIAL_EOS ; check end-of-segment offset
|
|
je done ; that's our EOS, so get out
|
|
CheckBit a,n ; is this a degenerate encoding?
|
|
jz try3 ; no, go for a wider encoding
|
|
DoMovs short,2
|
|
if n eq 7 ; are we finished with this byte?
|
|
lodsb ; (ah/al) is next input
|
|
xchg al,ah ; (al/ah) is next input
|
|
endif
|
|
Jump CmdAt,%((n + 1) mod 8) ; go process next command
|
|
done:
|
|
mov dl,n ; DL == current state
|
|
jmp DecodeDone ; exit
|
|
error:
|
|
;; Debug_Out "MRCI32 Decompress32: bad offset in LengthAt&n"
|
|
jmp DecodeError
|
|
|
|
align4
|
|
try3:
|
|
mov edx,ecx ; save delta
|
|
CheckBit a,%(n + 1) ; is this a 3-bit encoding?
|
|
jz try5 ; no, go for wider still
|
|
BitsAt %(n+2),1
|
|
DoMovs short,ecx,3
|
|
Jump CmdAt,%((n + 3) mod 8) ; go process next command
|
|
|
|
align4
|
|
try5:
|
|
CheckBit a,%(n + 2) ; is this a 5-bit encoding?
|
|
jz try7 ; no, go test for wider STILL
|
|
BitsAt %(n+3),2
|
|
DoMovs short,ecx,5
|
|
Jump CmdAt,%((n + 5) mod 8) ; go process next command
|
|
|
|
align4
|
|
try7:
|
|
CheckBit a,%(n + 3) ; is this a 7 bit encoding?
|
|
jz try9 ; no, go test for wider STILL
|
|
BitsAt %(n+4),3
|
|
DoMovs long,ecx,9
|
|
Jump CmdAt,%((n + 7) mod 8) ; go process next command
|
|
|
|
align4
|
|
try9:
|
|
CheckBit a,%(n + 4) ; is this a 9 bit encoding?
|
|
jz tryGeneral ; no, go handle generically
|
|
BitsAt %(n+5),4
|
|
DoMovs long,ecx,17
|
|
Jump CmdAt,%((n + 9) mod 8) ; go process next command
|
|
;
|
|
; Length exception handling code goes here
|
|
;
|
|
align4
|
|
tryGeneral:
|
|
mov cl,n+5 ; CL == # of bits to eat to yield
|
|
if n NE 7
|
|
jmp LengthAbove32 ; gamma length with 5 leading zeros stripped
|
|
else
|
|
;; .errnz $-GeneralLength ; assert that we'll fall through
|
|
endif
|
|
endm
|
|
|
|
|
|
DoGeneralLength macro
|
|
local try11,try13,try15,try17
|
|
|
|
public LengthAbove32,CopyString
|
|
|
|
GeneralLength:
|
|
align4
|
|
|
|
LengthAbove32:
|
|
shl eax,16 ;
|
|
mov ax,[esi] ; get 16 more bits
|
|
add cl,16 ;
|
|
ror eax,cl ; (eax) is filled, time to party
|
|
;
|
|
; Figure out the length and do a string op
|
|
;
|
|
try11:
|
|
shr eax,1 ; is it an 11-bit encoding?
|
|
jnc try13 ; no
|
|
and eax,1Fh ; mask off the numeric value
|
|
add eax,33 ;
|
|
xchg ecx,eax ; (ecx) now has string length
|
|
sub al,10 ; record # extra bits in this length
|
|
;
|
|
; At this point, (ecx) is the # of bytes to copy and (al) is the number of
|
|
; additional bits to eat for the particular gamma length.
|
|
;
|
|
; Good coding practices suggest that CopyString be at the end so that the
|
|
; other gamma decoders need not jump backwards to it, but if we assume
|
|
; that the longer strings are marginally less common, then it is marginally
|
|
; better to fall through on this, the smallest of the general cases.
|
|
;
|
|
align4
|
|
|
|
CopyString:
|
|
DoMovs long,ecx
|
|
|
|
mov dl,al ; (dl) == bit position in old ax
|
|
cmp dl,24 ; is it the max?
|
|
jb @F ; no
|
|
inc esi ; yes, need to skip 1 more whole byte
|
|
lodsw ; get new (ax) to restart state machine
|
|
sub dl,24 ; (dl) == new state
|
|
DecodeRestart
|
|
|
|
align4
|
|
@@:
|
|
cmp dl,16 ; did we exhaust the old ax?
|
|
jae @F ; yes
|
|
dec esi ; no,
|
|
add dl,8 ; but we know we exhausted the low byte
|
|
@@:
|
|
lodsw ; get new (ax) to restart state machine
|
|
sub dl,16 ; (dl) == new state
|
|
DecodeRestart
|
|
|
|
align4
|
|
try13:
|
|
shr eax,1 ; is it an 13-bit encoding?
|
|
jnc try15 ; no
|
|
and eax,3Fh ; mask off the numeric value
|
|
add eax,65 ;
|
|
xchg ecx,eax ; (cx) now has string length
|
|
sub al,8 ; record # extra bits in this length
|
|
jmp CopyString ;
|
|
|
|
align4
|
|
try15:
|
|
shr eax,1 ; is it an 15-bit encoding?
|
|
jnc try17 ; no
|
|
and eax,7Fh ; mask off the numeric value
|
|
add eax,129 ;
|
|
xchg ecx,eax ; (ecx) now has string length
|
|
sub al,6 ; record # extra bits in this length
|
|
jmp CopyString ;
|
|
|
|
align4
|
|
try17:
|
|
shr eax,1 ; is it an 17-bit encoding?
|
|
;; Debug_OutNC "MRCI32 Decompress32: invalid length"
|
|
jnc DecodeError ; no, ERROR
|
|
and eax,0FFh ; mask off the numeric value
|
|
add eax,257 ;
|
|
xchg ecx,eax ; (ecx) now has string length
|
|
sub al,4 ; record # extra bits in this length
|
|
jmp CopyString ;
|
|
endm
|
|
|
|
|
|
;** DoMovs - worker macro for LengthAt and DoGeneralLength
|
|
;
|
|
; <size> is either "short" or "long"; if short, then we don't
|
|
; bother trying to do a movsw/movsb combo (overhead swamps benefit);
|
|
; if long, we do.
|
|
;
|
|
; If <len> == 2, the offset to use is in (ecx). (edx) is trashed.
|
|
;
|
|
; Otherwise, the offset has been saved in (edx), and <len>
|
|
; is the size of the string to move (normally ecx). (ecx) and (edx)
|
|
; are trashed.
|
|
;
|
|
; <errjmp> is where to go if the expansion is going to overflow the
|
|
; destination buffer. DoMovs just passes this parameter along to the
|
|
; CheckOffset macro.
|
|
;
|
|
DoMovs macro size,len,extra,errjmp
|
|
local slower
|
|
|
|
ifidni <len>,<2>
|
|
|
|
mov edx,esi ; save (esi) in (edx)
|
|
mov esi,edi
|
|
sub esi,ecx
|
|
CheckOffset 2,errjmp ; check target offset
|
|
movsb ; don't do movsw,
|
|
movsb ; that doesn't handle overlap!
|
|
mov esi,edx ; restore (esi) from (edx)
|
|
|
|
else
|
|
|
|
ifnb <len>
|
|
ifdifi <len>,<ecx>
|
|
ifb <extra>
|
|
mov ecx,len
|
|
else
|
|
lea ecx,[len+extra]
|
|
endif
|
|
else
|
|
ifnb <extra>
|
|
add ecx,extra
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
mov ebx,esi ; save (esi) in (ebx)
|
|
mov esi,edi ;
|
|
sub esi,edx ; (esi) points to string to move
|
|
CheckOffset ecx,errjmp ; check target offset
|
|
|
|
ifidni <size>,<short>
|
|
rep movsb
|
|
elseifidni <size>,<long>
|
|
cmp edx,1 ; if the offset is 1,
|
|
je short slower ; then overlap forces us to do movsb
|
|
shr ecx,1
|
|
rep movsw
|
|
adc ecx,ecx
|
|
slower: rep movsb
|
|
else
|
|
.err <Bad DoMovs parameter: size>
|
|
endif
|
|
|
|
mov esi,ebx ; restore (esi) from (ebx)
|
|
|
|
endif
|
|
endm
|
|
|
|
|
|
;** CheckOffset - Verify offsets in ESI and EDI are ok for len bytes
|
|
;
|
|
; If "len" is blank, then CheckOffset simply does a 1-byte check.
|
|
; In the event of an error in any case, it branches to DecodeError.
|
|
;
|
|
LastErrSJump equ <DecodeError>
|
|
LastErrBJump equ <DecodeError>
|
|
|
|
|
|
CheckOffset macro len,errjmp
|
|
local tmp,jsjmp,jbjmp
|
|
|
|
IFDEF MAXDEBUG
|
|
cmp edi,[maxOffset]
|
|
jb short tmp
|
|
int 3
|
|
tmp:
|
|
ENDIF
|
|
|
|
ifnb <errjmp>
|
|
ErrSJump equ <errjmp>
|
|
else
|
|
ErrSJump catstr LastErrSJump
|
|
LastErrSJump equ <jsjmp>
|
|
endif
|
|
|
|
ifb <len>
|
|
dec ebp ; space remaining in destination buffer?
|
|
else
|
|
sub ebp,len ; space remaining in destination buffer?
|
|
endif
|
|
|
|
;; Debug_OutS "MRCI32 Decompress32: target buffer overflow"
|
|
|
|
jsjmp: js ErrSJump
|
|
|
|
IFDEF INLINE_LOWER_BOUND_CHECKING
|
|
;
|
|
; In-line bounds checking is disabled in favor of an invalid page fault
|
|
; handler. To use this code, be aware that EBX cannot be used by the
|
|
; decoding macros above (and it currently is!)
|
|
;
|
|
ifnb <len>
|
|
ifnb <errjmp>
|
|
ErrBJump equ <errjmp>
|
|
else
|
|
ErrBJump catstr LastErrBJump
|
|
LastErrBJump equ <jbjmp>
|
|
endif
|
|
|
|
cmp esi,ebx ; have we ventured before start of dest. buffer?
|
|
|
|
;; Debug_OutB "MRCI32 Decompress32: target buffer underflow"
|
|
|
|
jbjmp: jb ErrBJump
|
|
endif
|
|
|
|
ENDIF ;INLINE_LOWER_BOUND_CHECKING
|
|
|
|
endm
|
|
|
|
|
|
;* Misc. macros
|
|
|
|
Jump macro lab,tag
|
|
jmp lab&tag
|
|
endm
|
|
|
|
|
|
XlatChr macro ch
|
|
ror al,1
|
|
xor al,80h
|
|
endm
|
|
|
|
|
|
align4 macro
|
|
;
|
|
; This actually slowed down the real-mode decompressor, so some
|
|
; time will need to be spent verifying this is a real win... -JP
|
|
;
|
|
align 4
|
|
endm
|
|
|
|
|
|
CheckBit macro reg,bit
|
|
if bit lt 8
|
|
test reg&l,(1 shl bit)
|
|
else
|
|
test reg&h,(1 shl (bit-8))
|
|
endif
|
|
endm
|
|
|
|
|
|
CopyBits macro dst,src,n,cbits
|
|
shld dst,src,16-n
|
|
and e&dst,(1 shl cbits)-1
|
|
endm
|
|
|
|
|
|
;
|
|
; AX has the remaining bits, DL has the next state
|
|
;
|
|
DecodeRestart macro
|
|
|
|
IFDEF DEBUG
|
|
cmp dl,8
|
|
;; Debug_OutAE "MRCI32 Decompress32: bad decode state in DL"
|
|
ENDIF
|
|
movzx edx,dl
|
|
jmp aCmdAt[edx*4] ; go to correct state handler
|
|
endm
|
|
|
|
|
|
IFDEF MAXDEBUG
|
|
public maxOffset
|
|
maxOffset dd -1 ; handy for getting control at a specific point
|
|
ENDIF
|