unpack.asm
资源名称:DOS系统的源代码.rar [点击查看]
上传用户:xiaoan1112
上传日期:2013-04-11
资源大小:19621k
文件大小:14k
源码类别:
操作系统开发
开发平台:
Visual C++
- COMMENT #
- UNPACK.ASM
- Copyright (c) 1991 - Microsoft Corp.
- All rights reserved.
- Microsoft Confidential
- Functions for uncompressing an LZ compressed buffer.
- Created 11-28-89 johnhe
- TABS = 7
- END_COMMENT #
- RING_BUF_LEN EQU 4096
- UNPB_BUF_LEN EQU (512 * 17)
- INDEX_LEN EQU 2
- WIN_MAX_STR_LEN EQU 16
- LANG_MAX_STR_LEN EQU (16 + INDEX_LEN)
- MAX_STR_LEN EQU (LANG_MAX_STR_LEN + 2)
- LANGUAGE_COMPRESS EQU 1
- WINDOWS_COMPRESS EQU 2
- ; =========================================================================
- DOSSEG
- .MODEL LARGE,C
- .DATA
- ; =========================================================================
- ; UnpackSet has to allocated before a call to either ClearRingBuffer for
- ; Unpack. It will contain a ptr to a segment allocated for use by the
- ; unpack functions. When it is allocated it must be normalize to the
- ; allocated buffer + 16 so that it can be used with a zero offset by
- ; the unpack functions.
- ; =========================================================================
- PUBLIC UnpackSeg
- UnpackSeg dd (?)
- ; =========================================================================
- ; BUFFER_SEG is not a real segment in the program but is used to get the
- ; offsets into a buffer pointed to by UnpackSeg. By using this dummy segment
- ; it allows setting DS to WORD PTR UnpackSeg[2] and then accessing all of
- ; the area without using segment over-rides.
- ; =========================================================================
- BUFFER_SEG SEGMENT at 00
- ORG 0
- RingBuf db RING_BUF_LEN DUP (?) ; Must org @ 0
- RingBufEnd LABEL BYTE
- UnpackBuffer db UNPB_BUF_LEN DUP (?)
- UnpackBufEnd LABEL BYTE
- lToDo dd (?) ; Total bytes remaining in unpack buf
- lWritten dd (?) ; Keeps track of total unpacked bytes
- iOutFile dw (?) ; Open file handle passed by caller
- CtrlFlags dw (?) ; Currunt control byte from upack buf
- RingIndex dw (?) ; Start offset in RingBuffer
- Char db (?) ; Current char from packed buffer
- SplitPoint db (?) ; Determines where to start
- OldSplitPoint db (?) ; Saves last spit point
- BUFFER_SEG ENDS
- ; =========================================================================
- .CODE
- EXTRN fUpdateByteCount:FAR ; Must be explicitly FAR
- ; =========================================================================
- ; Sets up the ring buffer for a new file by filling the buffer with
- ; spaces and setting the ctrl flags and buffer index to known starting
- ; values.
- ;
- ; void ClearRingBuffer( int CompressType )
- ;
- ; ARGUMENTS: int - Compression type (0 == Language group, 1 == Windows)
- ; RETURNS: void
- ;
- ; RingIndex = MAX_STR_LEN (depending on compression type)
- ; CtrlFlags = 0;
- ; SplitPoint = 0;
- ; memset( RingBuffer, ' ', RING_BUF_LEN - MAX_STR_LEN );
- ;
- ; =========================================================================
- ClearRingBuffer PROC USES DS ES DI, CompressType:BYTE
- mov AX,WORD PTR UnpackSeg[2] ; Get the allocated segment
- mov DS,AX
- mov ES,AX
- ASSUME DS:BUFFER_SEG, ES:BUFFER_SEG
- xor AX,AX ; DS:AX -> Start of ring buff
- mov CtrlFlags,AX
- mov SplitPoint,AL
- mov DI,AX ; DS:DI -> Start of ring buff
- mov RingIndex, RING_BUF_LEN - LANG_MAX_STR_LEN
- cmp CompressType,WINDOWS_COMPRESS
- jne @F
- mov RingIndex, RING_BUF_LEN - WIN_MAX_STR_LEN
- @@:
- mov AX,2020h ; AX == ' '
- mov CX,(RING_BUF_LEN / 2) ; Storing words so use half
- cld
- rep stosw ; Fill buffer with ' ' chars
- ret
- ClearRingBuffer ENDP
- ; =========================================================================
- ; Uncompresses a portion of a compressed file which was compressed using
- ; LZ compression algorithm. Before the function is called the ring buffer
- ; and unpacked buffer must be allocated. Before each new file is unpacked
- ; a call must be done to ClearRingBuffer() to initialize the buffers and
- ; indices. As the buffer is unpacked it is written to the destination
- ; file when ever the index in the unpacked buffer reaches a point
- ; MAX_STR_LEN from the end of the buffer. Each time the buffer is flushed
- ; the ptr to the packed buffer must be normalized to prevent a possible
- ; segment wrap of the ptr.
- ;
- ; long Unpack( int iFile, char far *InBuf, long lPackedBytes )
- ;
- ; ARGUMENTS: iFile - Open DOS file handle to write the unpacked bytes
- ; InBuf - Ptr to buffer to be unpacked
- ; lPackedBytes - Length of the buffer in bytes
- ; RETURNS: long - Number of bytes written to the file.
- ;
- ;
- ; Register will be setup and used as follows within the function
- ;
- ; ES:BX -> Next byte in packed buffer
- ; DS:SI -> Ring buffer
- ; DS:DI -> Next byte in unpack buffer
- ; BP == CtrlFlags
- ; DX General use
- ; CX General use
- ; =========================================================================
- Unpack PROC USES DS ES DI SI,iFile:WORD, PackedBuf:PTR, lPackedBytes:DWORD
- mov AX,WORD PTR UnpackSeg[2]; Get the allocated unpack segment
- mov DS,AX
- ASSUME DS:BUFFER_SEG
- les AX,lPackedBytes ; ES:AX == number of bytes to unpack
- mov WORD PTR lToDo,AX ; Move number for faster access
- mov WORD PTR lToDo[2],ES ; lToDo = Total byte to unpacked
- mov AL,SplitPoint ; Save split point in case this is
- mov OldSplitPoint,AL ; a continuation of a previous unpack
- mov SI,RingIndex ; DS:SI -> current pos. in ring buffer
- mov AX,iFile ; Put iFile into new segment
- mov iOutFile,AX
- les BX,PackedBuf ; Get input buffer and normalize
- call NEAR PTR NormalizePackPtr ; ES:BX -> normalize packed buffer
- mov DI,OFFSET UnpackBuffer ; DS:DI -> Unpack buffer
- xor AX,AX ; Reset everything else to 0
- mov SplitPoint,AL
- mov WORD PTR lWritten,AX
- mov WORD PTR lWritten[2],AX
- push BP ; Save BP for cleanup later
- mov BP,CtrlFlags ; BP will always hold the ctrl flags
- cmp OldSplitPoint,1 ; See if this is a continuation
- je SplitPoint1 ; of a previous file and if so
- mov AL,Char ; may need last char from unpack buf
- cmp OldSplitPoint,2 ; to start at the place where it
- je SplitPoint2 ; was left off last time
- MainLoop: ; Start of loop which will continue until lToDo == 0
- mov AX,WORD PTR lToDo ; See if all bytes are unpacked
- or AX,WORD PTR lToDo[2]
- jnz CheckUnpackBuf ; Continue if both bytes != 0
- jmp UnpackExit ; Jump to successfull exit point
- CheckUnpackBuf: ; See if time to flush buffer
- and SI,0fffh ; Make ptr to wrap back to 0 if > 4095
- cmp DI,OFFSET UnpackBufEnd - MAX_STR_LEN
- jl GetNextChar ; Buffer not full yet
- call NEAR PTR FlushUnpackBuffer ; Else flush the buffer
- or AX,AX ; AX will be -1 if error else 0
- jz GetNextChar
- jmp ErrorExit
- GetNextChar:
- mov AL,ES:[BX] ; Get next char from in buf
- inc BX ; Increment packed buffer ptr
- sub WORD PTR lToDo,1 ; Decrement remaining packed bytes
- sbb WORD PTR lToDo[2],0
- AnyFlagsLeft:
- shr BP,1 ; High byte has bit mask of flags
- test BP,0ff00h ; Any flags left in this byte?
- jnz TestFlag ; If yes go forward and check the flag
- ; Set bit mask to for next 8 characters
- or AX,0ff00h ; Set all bits in high byte for count
- mov BP,AX
- mov AX,WORD PTR lToDo ; See if all bytes are unpacked
- or AX,WORD PTR lToDo[2]
- jnz SplitPoint1 ; Continue if both bytes != 0
- ; If we get here it means we ran out of characters in the
- ; unpacked buffer but there's still more to do on the next
- ; call so set up to continue where we left off and then
- ; flush the buffer and return. We we return on the next
- ; call we already have the new control word can continue
- ; where we left off
- mov SplitPoint,1 ; Set starting point for next call
- jmp SHORT UnpackExit
- SplitPoint1: ; At this point we just used the char
- mov AL,ES:[BX] ; as ctrlbyte and need another char
- inc BX ; Increment packed buffer ptr
- sub WORD PTR lToDo,1 ; Decrement remaining packed count
- sbb WORD PTR lToDo[2],0
- TestFlag:
- test BP,1 ; If flag == 1 character is not
- jz UnpackIt ; special so just copy it
- mov [DI],AL ; Store char in unpacked buffer
- mov [SI],AL ; Also store it in the ring buffer
- inc DI ; Point to next char in unpacked
- inc SI ; and ring buffer
- jmp MainLoop
- ; Flag bit was 0 so get a string from ring buf
- UnpackIt:
- mov DX,WORD PTR lToDo ; See if all bytes are unpacked
- or DX,WORD PTR lToDo[2]
- jnz SplitPoint2 ; Continue if both bytes != 0
- ; If we get here it means we ran out of characters in the
- ; unpacked buffer but there's still more to do on the next
- ; call so set up to continue where we left off and then
- ; flush the buffer and return. We we return on the next
- ; call we already have the first byte of the control
- ; word which determines the ringbuf offset and string count
- mov Char,AL ; Save the byte, we need it next call
- mov SplitPoint,2 ; Set starting point for next call
- jmp SHORT UnpackExit
- SplitPoint2: ; offset in ringbuf = ((next char & 0xf0) << 4) + AL
- ; length to copy = (nextchar & 0x0f) + 2)
- ; AL has last char from packed buffer
- mov AH,ES:[BX] ; Get the ctrl byte from packed buf
- inc BX ; Increment packed buffer ptr
- sub WORD PTR lToDo,1 ; Decrement remaining packed bytes
- sbb WORD PTR lToDo[2],0
- xor CX,CX ; Determine the str length in CX
- mov CL,AH ; CX = ctrl byte from the packed buf
- and CL,0fh ; Mask off the high nibble of low byte
- add CL,3
- ; inc CL ; Add 2
- ; inc CL ; CX == count of bytes to copy
- shr AH,1 ; Put hi nibble into low nibble of AH
- shr AH,1 ; and now AX will be a 12 bit value
- shr AH,1 ; which represents the offset in the
- shr AH,1 ; ring buffer
- CopyString:
- ; The string length may force a wrap of the ringbuffer so
- ; we have to anticipate this by having 2 methods of
- ; copying. If the ring buffer wraps we do a slow copy that
- ; normalizes SI after each movs instruction.
- ; Don't need a CX check because the value will always
- ; always be >= 3 because of the above ADD CX,3
- push ES ; Save the unpacked segment
- mov DX,SI ; DS:DX --> Dest ringbuf location
- mov SI,AX ; DS:SI --> Source ringbuf location
- mov AX,DS
- mov ES,AX ; ES:DI --> Dest unpackbuf location
- cld
- DoCopy:
- lodsb ; Get source byte from ring buffer
- stosb ; Copy byte to unpacked buffer
- xchg DI,DX ; DS:DI --> Dest ringbuf location
- stosb ; Copy byte to new ringbuf location
- xchg DI,DX ; DS:DI --> Next unpackbuf location
- and DX,0fffh ; Prevent wraps in the ring buffer
- and SI,0fffh ; Have to do both source ptrs
- loop DoCopy
- mov SI,DX ; DS:SI --> Next ringbuf offset
- pop ES ; ES:BX --> unpacked segment again
- jmp MainLoop
- ; ==========================================================
- ; Function exit points. If the unpacked buffer has anything
- ; copied to it which needs to be written it will be flushed
- ; if there were no errors.
- ; ==========================================================
- UnpackExit:
- cmp DI,OFFSET UnpackBuffer ; See if anything in unpack buffer
- je SetReturnCount
- call NEAR PTR FlushUnpackBuffer ; Need to flush one last time
- or AX,AX ; Check for error
- jnz ErrorExit
- SetReturnCount:
- les AX,lWritten
- mov DX,ES ; DX:AX == Unpacked bytes written
- jmp SHORT UnpackExit2
- ErrorExit:
- mov AX,-1 ; Signal error
- cwd ; DX:AX == -1
- UnpackExit2:
- mov RingIndex,SI ; Save current ring buffer index
- mov CtrlFlags,BP
- pop BP
- ret
- Unpack ENDP
- ; =========================================================================
- ; Flushes the unpack buffer to file and then resets the output buffer
- ; indices to be ready to starting filling the buffer again. After writing
- ; to the file a call is done to update the gage on the screen and
- ; then the unpacked ptr is normalized.
- ;
- ; int FlushUnpackBuffer( void )
- ;
- ; ARGUMENTS: NONE */
- ; RETURN: int - OK if disk write successfully else ERROR
- ; */
- ; =========================================================================
- FlushUnpackBuffer:
- push SI
- mov SI,BX ; Save unpacked offset for normalizing
- mov DX,OFFSET UnpackBuffer ; DS:DX --> Start of unpacked buffer
- sub DI,DX ; DI == count of bytes to write
- mov CX,DI ; CX == Count of byte to write
- mov AH,40h ; DOS write handle function
- mov BX,iOutFile ; BX = Open file handle
- int 21h
- jc FlushError ; Error check
- cmp AX,DI ; See if all bytes were written
- jne FlushError
- add WORD PTR lWritten,CX ; Update total bytes
- adc WORD PTR lWritten[2],0 ; written to disk
- ; Bytes were successfully written so setup and call
- ; the function to update the gage on the screen
- push DS
- push ES
- mov AX,@DATA ; Setup ES & DS to C data segment
- mov DS,AX
- mov ES,AX
- xor AX,AX
- push AX ; Put byte count on the stack
- push CX ; as a long value (dword)
- call fUpdateByteCount ; Update the status gage C function
- add SP,4 ; Adjust SP for 2 pushes
- pop ES
- pop DS
- mov BX,SI ; ES:BX --> packed buffer
- call NEAR PTR NormalizePackPtr ; ES:BX -> normalize packed buffer
- FlushExit:
- mov DI,OFFSET UnpackBuffer ; Reset DS:DI to start of unpack buf
- pop SI
- xor AX,AX ; Return OK
- ret
- FlushError:
- pop SI
- mov AX,-1 ; Signal error
- ret
- ; =========================================================================
- ; Normalizes ptr to packed buffer in ES:BX
- ; =========================================================================
- NormalizePackPtr:
- push AX
- push BX
- mov AX,ES ; AX:BX -> current packed buf location
- xchg AX,BX ; Now BX == Segment, AX = Offset
- shr AX,1 ; Divid offset in AX by 16
- shr AX,1
- shr AX,1
- shr AX,1
- add AX,BX ; Add segment to normalized offset
- mov ES,AX ; ES == Normalized segment
- pop BX
- pop AX
- and BX,0fh ; Mask all but 4 low bits of offset
- ret ; ES:BX --> normalized packed location
- ; Offset will be less than 16
- ; =========================================================================
- END