;** 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 equ 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 ; ; 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 == 2, the offset to use is in (ecx). (edx) is trashed. ; ; Otherwise, the offset has been saved in (edx), and ; is the size of the string to move (normally ecx). (ecx) and (edx) ; are trashed. ; ; 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 ,<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 ifdifi , ifb mov ecx,len else lea ecx,[len+extra] endif else ifnb 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 , rep movsb elseifidni , 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 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 LastErrBJump equ CheckOffset macro len,errjmp local tmp,jsjmp,jbjmp IFDEF MAXDEBUG cmp edi,[maxOffset] jb short tmp int 3 tmp: ENDIF ifnb ErrSJump equ else ErrSJump catstr LastErrSJump LastErrSJump equ endif ifb 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 ifnb ErrBJump equ else ErrBJump catstr LastErrBJump LastErrBJump equ 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